From dc3a09c1d02458a20248826cdfc955e7beedbb0f Mon Sep 17 00:00:00 2001 From: shirou Date: Sun, 22 Oct 2017 16:39:36 +0900 Subject: [PATCH 01/62] [host] linux: change file if in a lxc container. --- host/host_linux.go | 13 ++++++++++++- host/host_test.go | 2 ++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/host/host_linux.go b/host/host_linux.go index 14893a4..06d469b 100644 --- a/host/host_linux.go +++ b/host/host_linux.go @@ -95,7 +95,18 @@ func BootTime() (uint64, error) { if t != 0 { return t, nil } - filename := common.HostProc("stat") + + system, role, err := Virtualization() + if err != nil { + return 0, err + } + statFile := "stat" + if system == "lxc" && role == "guest" { + // if lxc, /proc/uptime is used. + statFile = "uptime" + } + + filename := common.HostProc(statFile) lines, err := common.ReadLines(filename) if err != nil { return 0, err diff --git a/host/host_test.go b/host/host_test.go index 65f7a75..d9e6611 100644 --- a/host/host_test.go +++ b/host/host_test.go @@ -40,11 +40,13 @@ func TestBoot_time(t *testing.T) { if v < 946652400 { t.Errorf("Invalid Boottime, older than 2000-01-01") } + t.Logf("first boot time: %d", v) v2, err := BootTime() if v != v2 { t.Errorf("cached boot time is different") } + t.Logf("second boot time: %d", v2) } func TestUsers(t *testing.T) { From 1be443f398239c0774fdfbec7b5818b52cd5f547 Mon Sep 17 00:00:00 2001 From: Tatiana Borisova Date: Thu, 14 Dec 2017 17:27:08 +0000 Subject: [PATCH 02/62] Fix build attempt 3 (add stub for host_cgo.go) --- host/host_cgo.go | 3 + host/host_darwin_cgo.go | 2 +- host/include/smc.c | 700 ------------------------------------------------ host/include/smc.h | 254 ------------------ host/smc.c | 700 ++++++++++++++++++++++++++++++++++++++++++++++++ host/smc.h | 254 ++++++++++++++++++ 6 files changed, 958 insertions(+), 955 deletions(-) create mode 100644 host/host_cgo.go delete mode 100644 host/include/smc.c delete mode 100644 host/include/smc.h create mode 100644 host/smc.c create mode 100644 host/smc.h diff --git a/host/host_cgo.go b/host/host_cgo.go new file mode 100644 index 0000000..c95dd23 --- /dev/null +++ b/host/host_cgo.go @@ -0,0 +1,3 @@ +// +build cgo + +package gopsutil diff --git a/host/host_darwin_cgo.go b/host/host_darwin_cgo.go index be5e18b..9471c3a 100644 --- a/host/host_darwin_cgo.go +++ b/host/host_darwin_cgo.go @@ -4,7 +4,7 @@ package host // #cgo LDFLAGS: -framework IOKit -// #include "include/smc.c" +// #include "smc.h" import "C" func SensorsTemperatures() ([]TemperatureStat, error) { diff --git a/host/include/smc.c b/host/include/smc.c deleted file mode 100644 index 30a232b..0000000 --- a/host/include/smc.c +++ /dev/null @@ -1,700 +0,0 @@ -/* - * Apple System Management Controller (SMC) API from user space for Intel based - * Macs. Works by talking to the AppleSMC.kext (kernel extension), the driver - * for the SMC. - * - * smc.c - * libsmc - * - * Copyright (C) 2014 beltex - * - * Based off of fork from: - * osx-cpu-temp - * - * With credits to: - * - * Copyright (C) 2006 devnull - * Apple System Management Control (SMC) Tool - * - * Copyright (C) 2006 Hendrik Holtmann - * smcFanControl - * - * This program 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 2 of the License, or - * (at your option) any later version. - * - * This program 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, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include -#include -#include "smc.h" - - -//------------------------------------------------------------------------------ -// MARK: MACROS -//------------------------------------------------------------------------------ - - -/** -Name of the SMC IOService as seen in the IORegistry. You can view it either via -command line with ioreg or through the IORegistryExplorer app (found on Apple's -developer site - Hardware IO Tools for Xcode) -*/ -#define IOSERVICE_SMC "AppleSMC" - - -/** -IOService for getting machine model name -*/ -#define IOSERVICE_MODEL "IOPlatformExpertDevice" - - -/** -SMC data types - 4 byte multi-character constants - -Sources: See TMP SMC keys in smc.h - -http://stackoverflow.com/questions/22160746/fpe2-and-sp78-data-types -*/ -#define DATA_TYPE_UINT8 "ui8 " -#define DATA_TYPE_UINT16 "ui16" -#define DATA_TYPE_UINT32 "ui32" -#define DATA_TYPE_FLAG "flag" -#define DATA_TYPE_FPE2 "fpe2" -#define DATA_TYPE_SFDS "{fds" -#define DATA_TYPE_SP78 "sp78" - - -//------------------------------------------------------------------------------ -// MARK: GLOBAL VARS -//------------------------------------------------------------------------------ - - -/** -Our connection to the SMC -*/ -static io_connect_t conn; - - -/** -Number of characters in an SMC key -*/ -static const int SMC_KEY_SIZE = 4; - - -/** -Number of characters in a data type "key" returned from the SMC. See data type -macros. -*/ -static const int DATA_TYPE_SIZE = 4; - - -//------------------------------------------------------------------------------ -// MARK: ENUMS -//------------------------------------------------------------------------------ - - -/** -Defined by AppleSMC.kext. See SMCParamStruct. - -These are SMC specific return codes -*/ -typedef enum { - kSMCSuccess = 0, - kSMCError = 1, - kSMCKeyNotFound = 0x84 -} kSMC_t; - - -/** -Defined by AppleSMC.kext. See SMCParamStruct. - -Function selectors. Used to tell the SMC which function inside it to call. -*/ -typedef enum { - kSMCUserClientOpen = 0, - kSMCUserClientClose = 1, - kSMCHandleYPCEvent = 2, - kSMCReadKey = 5, - kSMCWriteKey = 6, - kSMCGetKeyCount = 7, - kSMCGetKeyFromIndex = 8, - kSMCGetKeyInfo = 9 -} selector_t; - - -//------------------------------------------------------------------------------ -// MARK: STRUCTS -//------------------------------------------------------------------------------ - - -/** -Defined by AppleSMC.kext. See SMCParamStruct. -*/ -typedef struct { - unsigned char major; - unsigned char minor; - unsigned char build; - unsigned char reserved; - unsigned short release; -} SMCVersion; - - -/** -Defined by AppleSMC.kext. See SMCParamStruct. -*/ -typedef struct { - uint16_t version; - uint16_t length; - uint32_t cpuPLimit; - uint32_t gpuPLimit; - uint32_t memPLimit; -} SMCPLimitData; - - -/** -Defined by AppleSMC.kext. See SMCParamStruct. - -- dataSize : How many values written to SMCParamStruct.bytes -- dataType : Type of data written to SMCParamStruct.bytes. This lets us know how - to interpret it (translate it to human readable) -*/ -typedef struct { - IOByteCount dataSize; - uint32_t dataType; - uint8_t dataAttributes; -} SMCKeyInfoData; - - -/** -Defined by AppleSMC.kext. - -This is the predefined struct that must be passed to communicate with the -AppleSMC driver. While the driver is closed source, the definition of this -struct happened to appear in the Apple PowerManagement project at around -version 211, and soon after disappeared. It can be seen in the PrivateLib.c -file under pmconfigd. - -https://www.opensource.apple.com/source/PowerManagement/PowerManagement-211/ -*/ -typedef struct { - uint32_t key; - SMCVersion vers; - SMCPLimitData pLimitData; - SMCKeyInfoData keyInfo; - uint8_t result; - uint8_t status; - uint8_t data8; - uint32_t data32; - uint8_t bytes[32]; -} SMCParamStruct; - - -/** -Used for returning data from the SMC. -*/ -typedef struct { - uint8_t data[32]; - uint32_t dataType; - uint32_t dataSize; - kSMC_t kSMC; -} smc_return_t; - - -//------------------------------------------------------------------------------ -// MARK: HELPERS - TYPE CONVERSION -//------------------------------------------------------------------------------ - - -/** -Convert data from SMC of fpe2 type to human readable. - -:param: data Data from the SMC to be converted. Assumed data size of 2. -:returns: Converted data -*/ -static unsigned int from_fpe2(uint8_t data[32]) -{ - unsigned int ans = 0; - - // Data type for fan calls - fpe2 - // This is assumend to mean floating point, with 2 exponent bits - // http://stackoverflow.com/questions/22160746/fpe2-and-sp78-data-types - ans += data[0] << 6; - ans += data[1] << 2; - - return ans; -} - - -/** -Convert to fpe2 data type to be passed to SMC. - -:param: val Value to convert -:param: data Pointer to data array to place result -*/ -static void to_fpe2(unsigned int val, uint8_t *data) -{ - data[0] = val >> 6; - data[1] = (val << 2) ^ (data[0] << 8); -} - - -/** -Convert SMC key to uint32_t. This must be done to pass it to the SMC. - -:param: key The SMC key to convert -:returns: uint32_t translation. - Returns zero if key is not 4 characters in length. -*/ -static uint32_t to_uint32_t(char *key) -{ - uint32_t ans = 0; - uint32_t shift = 24; - - // SMC key is expected to be 4 bytes - thus 4 chars - if (strlen(key) != SMC_KEY_SIZE) { - return 0; - } - - for (int i = 0; i < SMC_KEY_SIZE; i++) { - ans += key[i] << shift; - shift -= 8; - } - - return ans; -} - - -/** -For converting the dataType return from the SMC to human readable 4 byte -multi-character constant. -*/ -static void to_string(uint32_t val, char *dataType) -{ - int shift = 24; - - for (int i = 0; i < DATA_TYPE_SIZE; i++) { - // To get each char, we shift it into the lower 8 bits, and then & by - // 255 to insolate it - dataType[i] = (val >> shift) & 0xff; - shift -= 8; - } -} - - -//------------------------------------------------------------------------------ -// MARK: HELPERS - TMP CONVERSION -//------------------------------------------------------------------------------ - - -/** -Celsius to Fahrenheit -*/ -static double to_fahrenheit(double tmp) -{ - // http://en.wikipedia.org/wiki/Fahrenheit#Definition_and_conversions - return (tmp * 1.8) + 32; -} - - -/** -Celsius to Kelvin -*/ -static double to_kelvin(double tmp) -{ - // http://en.wikipedia.org/wiki/Kelvin - return tmp + 273.15; -} - - -//------------------------------------------------------------------------------ -// MARK: "PRIVATE" FUNCTIONS -//------------------------------------------------------------------------------ - - -/** -Make a call to the SMC - -:param: inputStruct Struct that holds data telling the SMC what you want -:param: outputStruct Struct holding the SMC's response -:returns: I/O Kit return code -*/ -static kern_return_t call_smc(SMCParamStruct *inputStruct, - SMCParamStruct *outputStruct) -{ - kern_return_t result; - size_t inputStructCnt = sizeof(SMCParamStruct); - size_t outputStructCnt = sizeof(SMCParamStruct); - - result = IOConnectCallStructMethod(conn, kSMCHandleYPCEvent, - inputStruct, - inputStructCnt, - outputStruct, - &outputStructCnt); - - if (result != kIOReturnSuccess) { - // IOReturn error code lookup. See "Accessing Hardware From Applications - // -> Handling Errors" Apple doc - result = err_get_code(result); - } - - return result; -} - - -/** -Read data from the SMC - -:param: key The SMC key -*/ -static kern_return_t read_smc(char *key, smc_return_t *result_smc) -{ - kern_return_t result; - SMCParamStruct inputStruct; - SMCParamStruct outputStruct; - - memset(&inputStruct, 0, sizeof(SMCParamStruct)); - memset(&outputStruct, 0, sizeof(SMCParamStruct)); - memset(result_smc, 0, sizeof(smc_return_t)); - - // First call to AppleSMC - get key info - inputStruct.key = to_uint32_t(key); - inputStruct.data8 = kSMCGetKeyInfo; - - result = call_smc(&inputStruct, &outputStruct); - result_smc->kSMC = outputStruct.result; - - if (result != kIOReturnSuccess || outputStruct.result != kSMCSuccess) { - return result; - } - - // Store data for return - result_smc->dataSize = outputStruct.keyInfo.dataSize; - result_smc->dataType = outputStruct.keyInfo.dataType; - - - // Second call to AppleSMC - now we can get the data - inputStruct.keyInfo.dataSize = outputStruct.keyInfo.dataSize; - inputStruct.data8 = kSMCReadKey; - - result = call_smc(&inputStruct, &outputStruct); - result_smc->kSMC = outputStruct.result; - - if (result != kIOReturnSuccess || outputStruct.result != kSMCSuccess) { - return result; - } - - memcpy(result_smc->data, outputStruct.bytes, sizeof(outputStruct.bytes)); - - return result; -} - - -/** -Write data to the SMC. - -:returns: IOReturn IOKit return code -*/ -static kern_return_t write_smc(char *key, smc_return_t *result_smc) -{ - kern_return_t result; - SMCParamStruct inputStruct; - SMCParamStruct outputStruct; - - memset(&inputStruct, 0, sizeof(SMCParamStruct)); - memset(&outputStruct, 0, sizeof(SMCParamStruct)); - - // First call to AppleSMC - get key info - inputStruct.key = to_uint32_t(key); - inputStruct.data8 = kSMCGetKeyInfo; - - result = call_smc(&inputStruct, &outputStruct); - result_smc->kSMC = outputStruct.result; - - if (result != kIOReturnSuccess || outputStruct.result != kSMCSuccess) { - return result; - } - - // Check data is correct - if (result_smc->dataSize != outputStruct.keyInfo.dataSize || - result_smc->dataType != outputStruct.keyInfo.dataType) { - return kIOReturnBadArgument; - } - - // Second call to AppleSMC - now we can write the data - inputStruct.data8 = kSMCWriteKey; - inputStruct.keyInfo.dataSize = outputStruct.keyInfo.dataSize; - - // Set data to write - memcpy(inputStruct.bytes, result_smc->data, sizeof(result_smc->data)); - - result = call_smc(&inputStruct, &outputStruct); - result_smc->kSMC = outputStruct.result; - - return result; -} - - -/** -Get the model name of the machine. -*/ -static kern_return_t get_machine_model(io_name_t model) -{ - io_service_t service; - kern_return_t result; - - service = IOServiceGetMatchingService(kIOMasterPortDefault, - IOServiceMatching(IOSERVICE_MODEL)); - - if (service == 0) { - printf("ERROR: %s NOT FOUND\n", IOSERVICE_MODEL); - return kIOReturnError; - } - - // Get the model name - result = IORegistryEntryGetName(service, model); - IOObjectRelease(service); - - return result; -} - - -//------------------------------------------------------------------------------ -// MARK: "PUBLIC" FUNCTIONS -//------------------------------------------------------------------------------ - - -kern_return_t open_smc(void) -{ - kern_return_t result; - io_service_t service; - - service = IOServiceGetMatchingService(kIOMasterPortDefault, - IOServiceMatching(IOSERVICE_SMC)); - - if (service == 0) { - // NOTE: IOServiceMatching documents 0 on failure - printf("ERROR: %s NOT FOUND\n", IOSERVICE_SMC); - return kIOReturnError; - } - - result = IOServiceOpen(service, mach_task_self(), 0, &conn); - IOObjectRelease(service); - - return result; -} - - -kern_return_t close_smc(void) -{ - return IOServiceClose(conn); -} - - -bool is_key_valid(char *key) -{ - bool ans = false; - kern_return_t result; - smc_return_t result_smc; - - if (strlen(key) != SMC_KEY_SIZE) { - printf("ERROR: Invalid key size - must be 4 chars\n"); - return ans; - } - - // Try a read and see if it succeeds - result = read_smc(key, &result_smc); - - if (result == kIOReturnSuccess && result_smc.kSMC == kSMCSuccess) { - ans = true; - } - - return ans; -} - - -double get_tmp(char *key, tmp_unit_t unit) -{ - kern_return_t result; - smc_return_t result_smc; - - result = read_smc(key, &result_smc); - - if (!(result == kIOReturnSuccess && - result_smc.dataSize == 2 && - result_smc.dataType == to_uint32_t(DATA_TYPE_SP78))) { - // Error - return 0.0; - } - - // TODO: Create from_sp78() convert function - double tmp = result_smc.data[0]; - - switch (unit) { - case CELSIUS: - break; - case FAHRENHEIT: - tmp = to_fahrenheit(tmp); - break; - case KELVIN: - tmp = to_kelvin(tmp); - break; - } - - return tmp; -} - - -bool is_battery_powered(void) -{ - kern_return_t result; - smc_return_t result_smc; - - result = read_smc(BATT_PWR, &result_smc); - - if (!(result == kIOReturnSuccess && - result_smc.dataSize == 1 && - result_smc.dataType == to_uint32_t(DATA_TYPE_FLAG))) { - // Error - return false; - } - - return result_smc.data[0]; -} - - -bool is_optical_disk_drive_full(void) -{ - kern_return_t result; - smc_return_t result_smc; - - result = read_smc(ODD_FULL, &result_smc); - - if (!(result == kIOReturnSuccess && - result_smc.dataSize == 1 && - result_smc.dataType == to_uint32_t(DATA_TYPE_FLAG))) { - // Error - return false; - } - - return result_smc.data[0]; -} - - -//------------------------------------------------------------------------------ -// MARK: FAN FUNCTIONS -//------------------------------------------------------------------------------ - - -bool get_fan_name(unsigned int fan_num, fan_name_t name) -{ - char key[5]; - kern_return_t result; - smc_return_t result_smc; - - sprintf(key, "F%dID", fan_num); - result = read_smc(key, &result_smc); - - if (!(result == kIOReturnSuccess && - result_smc.dataSize == 16 && - result_smc.dataType == to_uint32_t(DATA_TYPE_SFDS))) { - return false; - } - - - /* - We know the data size is 16 bytes and the type is "{fds", a custom - struct defined by the AppleSMC.kext. See TMP enum sources for the - struct. - - The last 12 bytes contain the name of the fan, an array of chars, hence - the loop range. - */ - int index = 0; - for (int i = 4; i < 16; i++) { - // Check if at the end (name may not be full 12 bytes) - // Could check for 0 (null), but instead we check for 32 (space). This - // is a hack to remove whitespace. :) - if (result_smc.data[i] == 32) { - break; - } - - name[index] = result_smc.data[i]; - index++; - } - - return true; -} - - -int get_num_fans(void) -{ - kern_return_t result; - smc_return_t result_smc; - - result = read_smc(NUM_FANS, &result_smc); - - if (!(result == kIOReturnSuccess && - result_smc.dataSize == 1 && - result_smc.dataType == to_uint32_t(DATA_TYPE_UINT8))) { - // Error - return -1; - } - - return result_smc.data[0]; -} - - -unsigned int get_fan_rpm(unsigned int fan_num) -{ - char key[5]; - kern_return_t result; - smc_return_t result_smc; - - sprintf(key, "F%dAc", fan_num); - result = read_smc(key, &result_smc); - - if (!(result == kIOReturnSuccess && - result_smc.dataSize == 2 && - result_smc.dataType == to_uint32_t(DATA_TYPE_FPE2))) { - // Error - return 0; - } - - return from_fpe2(result_smc.data); -} - - -bool set_fan_min_rpm(unsigned int fan_num, unsigned int rpm, bool auth) -{ - // TODO: Add rpm val safety check - char key[5]; - bool ans = false; - kern_return_t result; - smc_return_t result_smc; - - memset(&result_smc, 0, sizeof(smc_return_t)); - - // TODO: Don't use magic number - result_smc.dataSize = 2; - result_smc.dataType = to_uint32_t(DATA_TYPE_FPE2); - to_fpe2(rpm, result_smc.data); - - sprintf(key, "F%dMn", fan_num); - result = write_smc(key, &result_smc); - - if (result == kIOReturnSuccess && result_smc.kSMC == kSMCSuccess) { - ans = true; - } - - return ans; -} diff --git a/host/include/smc.h b/host/include/smc.h deleted file mode 100644 index b156368..0000000 --- a/host/include/smc.h +++ /dev/null @@ -1,254 +0,0 @@ -/* - * Apple System Management Controller (SMC) API from user space for Intel based - * Macs. Works by talking to the AppleSMC.kext (kernel extension), the driver - * for the SMC. - * - * smc.h - * libsmc - * - * Copyright (C) 2014 beltex - * - * Based off of fork from: - * osx-cpu-temp - * - * With credits to: - * - * Copyright (C) 2006 devnull - * Apple System Management Control (SMC) Tool - * - * Copyright (C) 2006 Hendrik Holtmann - * smcFanControl - * - * This program 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 2 of the License, or - * (at your option) any later version. - * - * This program 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, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include - - -//------------------------------------------------------------------------------ -// MARK: MACROS -//------------------------------------------------------------------------------ - - -/** -SMC keys for temperature sensors - 4 byte multi-character constants - -Not applicable to all Mac's of course. In adition, the definition of the codes -may not be 100% accurate necessarily. Finally, list is incomplete. - -Presumed letter translations: - -- T = Temperature (if first char) -- C = CPU -- G = GPU -- P = Proximity -- D = Diode -- H = Heatsink - -Sources: - -- https://www.apple.com/downloads/dashboard/status/istatpro.html -- https://github.com/hholtmann/smcFanControl -- https://github.com/jedda/OSX-Monitoring-Tools -- http://www.parhelia.ch/blog/statics/k3_keys.html -*/ -#define AMBIENT_AIR_0 "TA0P" -#define AMBIENT_AIR_1 "TA1P" -#define CPU_0_DIODE "TC0D" -#define CPU_0_HEATSINK "TC0H" -#define CPU_0_PROXIMITY "TC0P" -#define ENCLOSURE_BASE_0 "TB0T" -#define ENCLOSURE_BASE_1 "TB1T" -#define ENCLOSURE_BASE_2 "TB2T" -#define ENCLOSURE_BASE_3 "TB3T" -#define GPU_0_DIODE "TG0D" -#define GPU_0_HEATSINK "TG0H" -#define GPU_0_PROXIMITY "TG0P" -#define HARD_DRIVE_BAY "TH0P" -#define MEMORY_SLOT_0 "TM0S" -#define MEMORY_SLOTS_PROXIMITY "TM0P" -#define NORTHBRIDGE "TN0H" -#define NORTHBRIDGE_DIODE "TN0D" -#define NORTHBRIDGE_PROXIMITY "TN0P" -#define THUNDERBOLT_0 "TI0P" -#define THUNDERBOLT_1 "TI1P" -#define WIRELESS_MODULE "TW0P" - - -/** -SMC keys for fans - 4 byte multi-character constants - -Number of fans on Macs vary of course, thus not all keys will be applicable. - -Presumed letter translations: - -- F = Fan -- Ac = Acutal -- Mn = Min -- Mx = Max -- Sf = Safe -- Tg = Target - -Sources: See TMP SMC keys -*/ -#define FAN_0 "F0Ac" -#define FAN_0_MIN_RPM "F0Mn" -#define FAN_0_MAX_RPM "F0Mx" -#define FAN_0_SAFE_RPM "F0Sf" -#define FAN_0_TARGET_RPM "F0Tg" -#define FAN_1 "F1Ac" -#define FAN_1_MIN_RPM "F1Mn" -#define FAN_1_MAX_RPM "F1Mx" -#define FAN_1_SAFE_RPM "F1Sf" -#define FAN_1_TARGET_RPM "F1Tg" -#define FAN_2 "F2Ac" -#define FAN_2_MIN_RPM "F2Mn" -#define FAN_2_MAX_RPM "F2Mx" -#define FAN_2_SAFE_RPM "F2Sf" -#define FAN_2_TARGET_RPM "F2Tg" -#define NUM_FANS "FNum" -#define FORCE_BITS "FS! " - - -/** -Misc SMC keys - 4 byte multi-character constants - -Sources: See TMP SMC keys -*/ -#define BATT_PWR "BATP" -#define NUM_KEYS "#KEY" -#define ODD_FULL "MSDI" - - -//------------------------------------------------------------------------------ -// MARK: TYPES -//------------------------------------------------------------------------------ - - -typedef char fan_name_t[13]; - - -//------------------------------------------------------------------------------ -// MARK: ENUMS -//------------------------------------------------------------------------------ - - -typedef enum { - CELSIUS, - FAHRENHEIT, - KELVIN -} tmp_unit_t; - - -//------------------------------------------------------------------------------ -// MARK: PROTOTYPES -//------------------------------------------------------------------------------ - - -/** -Open a connection to the SMC - -:returns: kIOReturnSuccess on successful connection to the SMC. -*/ -kern_return_t open_smc(void); - - -/** -Close connection to the SMC - -:returns: kIOReturnSuccess on successful close of connection to the SMC. -*/ -kern_return_t close_smc(void); - - -/** -Check if an SMC key is valid. Useful for determining if a certain machine has -particular sensor or fan for example. - -:param: key The SMC key to check. 4 byte multi-character constant. Must be 4 - characters in length. -:returns: True if the key is found, false otherwise -*/ -bool is_key_valid(char *key); - - -/** -Get the current temperature from a sensor - -:param: key The temperature sensor to read from -:param: unit The unit for the temperature value. -:returns: Temperature of sensor. If the sensor is not found, or an error - occurs, return will be zero -*/ -double get_tmp(char *key, tmp_unit_t unit); - - -/** -Is the machine being powered by the battery? - -:returns: True if it is, false otherwise -*/ -bool is_battery_powered(void); - - -/** -Is there a CD in the optical disk drive (ODD)? - -:returns: True if there is, false otherwise -*/ -bool is_optical_disk_drive_full(void); - - -/** -Get the name of a fan. - -:param: fanNum The number of the fan to check -:param: name The name of the fan. Return will be empty on error. -:returns: True if successful, false otherwise. -*/ -bool get_fan_name(unsigned int fan_num, fan_name_t name); - - -/** -Get the number of fans on this machine. - -:returns: The number of fans. If an error occurs, return will be -1. -*/ -int get_num_fans(void); - - -/** -Get the current speed (RPM - revolutions per minute) of a fan. - -:param: fan_num The number of the fan to check -:returns: The fan RPM. If the fan is not found, or an error occurs, return - will be zero -*/ -UInt get_fan_rpm(UInt fan_num); - - -/** -Set the minimum speed (RPM - revolutions per minute) of a fan. This method -requires root privileges. By minimum we mean that OS X can interject and -raise the fan speed if needed, however it will not go below this. - -WARNING: You are playing with hardware here, BE CAREFUL. - -:param: fan_num The number of the fan to set -:param: rpm The speed you would like to set the fan to. -:param: auth Should the function do authentication? -:return: True if successful, false otherwise -*/ -bool set_fan_min_rpm(unsigned int fan_num, unsigned int rpm, bool auth); diff --git a/host/smc.c b/host/smc.c new file mode 100644 index 0000000..30a232b --- /dev/null +++ b/host/smc.c @@ -0,0 +1,700 @@ +/* + * Apple System Management Controller (SMC) API from user space for Intel based + * Macs. Works by talking to the AppleSMC.kext (kernel extension), the driver + * for the SMC. + * + * smc.c + * libsmc + * + * Copyright (C) 2014 beltex + * + * Based off of fork from: + * osx-cpu-temp + * + * With credits to: + * + * Copyright (C) 2006 devnull + * Apple System Management Control (SMC) Tool + * + * Copyright (C) 2006 Hendrik Holtmann + * smcFanControl + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include "smc.h" + + +//------------------------------------------------------------------------------ +// MARK: MACROS +//------------------------------------------------------------------------------ + + +/** +Name of the SMC IOService as seen in the IORegistry. You can view it either via +command line with ioreg or through the IORegistryExplorer app (found on Apple's +developer site - Hardware IO Tools for Xcode) +*/ +#define IOSERVICE_SMC "AppleSMC" + + +/** +IOService for getting machine model name +*/ +#define IOSERVICE_MODEL "IOPlatformExpertDevice" + + +/** +SMC data types - 4 byte multi-character constants + +Sources: See TMP SMC keys in smc.h + +http://stackoverflow.com/questions/22160746/fpe2-and-sp78-data-types +*/ +#define DATA_TYPE_UINT8 "ui8 " +#define DATA_TYPE_UINT16 "ui16" +#define DATA_TYPE_UINT32 "ui32" +#define DATA_TYPE_FLAG "flag" +#define DATA_TYPE_FPE2 "fpe2" +#define DATA_TYPE_SFDS "{fds" +#define DATA_TYPE_SP78 "sp78" + + +//------------------------------------------------------------------------------ +// MARK: GLOBAL VARS +//------------------------------------------------------------------------------ + + +/** +Our connection to the SMC +*/ +static io_connect_t conn; + + +/** +Number of characters in an SMC key +*/ +static const int SMC_KEY_SIZE = 4; + + +/** +Number of characters in a data type "key" returned from the SMC. See data type +macros. +*/ +static const int DATA_TYPE_SIZE = 4; + + +//------------------------------------------------------------------------------ +// MARK: ENUMS +//------------------------------------------------------------------------------ + + +/** +Defined by AppleSMC.kext. See SMCParamStruct. + +These are SMC specific return codes +*/ +typedef enum { + kSMCSuccess = 0, + kSMCError = 1, + kSMCKeyNotFound = 0x84 +} kSMC_t; + + +/** +Defined by AppleSMC.kext. See SMCParamStruct. + +Function selectors. Used to tell the SMC which function inside it to call. +*/ +typedef enum { + kSMCUserClientOpen = 0, + kSMCUserClientClose = 1, + kSMCHandleYPCEvent = 2, + kSMCReadKey = 5, + kSMCWriteKey = 6, + kSMCGetKeyCount = 7, + kSMCGetKeyFromIndex = 8, + kSMCGetKeyInfo = 9 +} selector_t; + + +//------------------------------------------------------------------------------ +// MARK: STRUCTS +//------------------------------------------------------------------------------ + + +/** +Defined by AppleSMC.kext. See SMCParamStruct. +*/ +typedef struct { + unsigned char major; + unsigned char minor; + unsigned char build; + unsigned char reserved; + unsigned short release; +} SMCVersion; + + +/** +Defined by AppleSMC.kext. See SMCParamStruct. +*/ +typedef struct { + uint16_t version; + uint16_t length; + uint32_t cpuPLimit; + uint32_t gpuPLimit; + uint32_t memPLimit; +} SMCPLimitData; + + +/** +Defined by AppleSMC.kext. See SMCParamStruct. + +- dataSize : How many values written to SMCParamStruct.bytes +- dataType : Type of data written to SMCParamStruct.bytes. This lets us know how + to interpret it (translate it to human readable) +*/ +typedef struct { + IOByteCount dataSize; + uint32_t dataType; + uint8_t dataAttributes; +} SMCKeyInfoData; + + +/** +Defined by AppleSMC.kext. + +This is the predefined struct that must be passed to communicate with the +AppleSMC driver. While the driver is closed source, the definition of this +struct happened to appear in the Apple PowerManagement project at around +version 211, and soon after disappeared. It can be seen in the PrivateLib.c +file under pmconfigd. + +https://www.opensource.apple.com/source/PowerManagement/PowerManagement-211/ +*/ +typedef struct { + uint32_t key; + SMCVersion vers; + SMCPLimitData pLimitData; + SMCKeyInfoData keyInfo; + uint8_t result; + uint8_t status; + uint8_t data8; + uint32_t data32; + uint8_t bytes[32]; +} SMCParamStruct; + + +/** +Used for returning data from the SMC. +*/ +typedef struct { + uint8_t data[32]; + uint32_t dataType; + uint32_t dataSize; + kSMC_t kSMC; +} smc_return_t; + + +//------------------------------------------------------------------------------ +// MARK: HELPERS - TYPE CONVERSION +//------------------------------------------------------------------------------ + + +/** +Convert data from SMC of fpe2 type to human readable. + +:param: data Data from the SMC to be converted. Assumed data size of 2. +:returns: Converted data +*/ +static unsigned int from_fpe2(uint8_t data[32]) +{ + unsigned int ans = 0; + + // Data type for fan calls - fpe2 + // This is assumend to mean floating point, with 2 exponent bits + // http://stackoverflow.com/questions/22160746/fpe2-and-sp78-data-types + ans += data[0] << 6; + ans += data[1] << 2; + + return ans; +} + + +/** +Convert to fpe2 data type to be passed to SMC. + +:param: val Value to convert +:param: data Pointer to data array to place result +*/ +static void to_fpe2(unsigned int val, uint8_t *data) +{ + data[0] = val >> 6; + data[1] = (val << 2) ^ (data[0] << 8); +} + + +/** +Convert SMC key to uint32_t. This must be done to pass it to the SMC. + +:param: key The SMC key to convert +:returns: uint32_t translation. + Returns zero if key is not 4 characters in length. +*/ +static uint32_t to_uint32_t(char *key) +{ + uint32_t ans = 0; + uint32_t shift = 24; + + // SMC key is expected to be 4 bytes - thus 4 chars + if (strlen(key) != SMC_KEY_SIZE) { + return 0; + } + + for (int i = 0; i < SMC_KEY_SIZE; i++) { + ans += key[i] << shift; + shift -= 8; + } + + return ans; +} + + +/** +For converting the dataType return from the SMC to human readable 4 byte +multi-character constant. +*/ +static void to_string(uint32_t val, char *dataType) +{ + int shift = 24; + + for (int i = 0; i < DATA_TYPE_SIZE; i++) { + // To get each char, we shift it into the lower 8 bits, and then & by + // 255 to insolate it + dataType[i] = (val >> shift) & 0xff; + shift -= 8; + } +} + + +//------------------------------------------------------------------------------ +// MARK: HELPERS - TMP CONVERSION +//------------------------------------------------------------------------------ + + +/** +Celsius to Fahrenheit +*/ +static double to_fahrenheit(double tmp) +{ + // http://en.wikipedia.org/wiki/Fahrenheit#Definition_and_conversions + return (tmp * 1.8) + 32; +} + + +/** +Celsius to Kelvin +*/ +static double to_kelvin(double tmp) +{ + // http://en.wikipedia.org/wiki/Kelvin + return tmp + 273.15; +} + + +//------------------------------------------------------------------------------ +// MARK: "PRIVATE" FUNCTIONS +//------------------------------------------------------------------------------ + + +/** +Make a call to the SMC + +:param: inputStruct Struct that holds data telling the SMC what you want +:param: outputStruct Struct holding the SMC's response +:returns: I/O Kit return code +*/ +static kern_return_t call_smc(SMCParamStruct *inputStruct, + SMCParamStruct *outputStruct) +{ + kern_return_t result; + size_t inputStructCnt = sizeof(SMCParamStruct); + size_t outputStructCnt = sizeof(SMCParamStruct); + + result = IOConnectCallStructMethod(conn, kSMCHandleYPCEvent, + inputStruct, + inputStructCnt, + outputStruct, + &outputStructCnt); + + if (result != kIOReturnSuccess) { + // IOReturn error code lookup. See "Accessing Hardware From Applications + // -> Handling Errors" Apple doc + result = err_get_code(result); + } + + return result; +} + + +/** +Read data from the SMC + +:param: key The SMC key +*/ +static kern_return_t read_smc(char *key, smc_return_t *result_smc) +{ + kern_return_t result; + SMCParamStruct inputStruct; + SMCParamStruct outputStruct; + + memset(&inputStruct, 0, sizeof(SMCParamStruct)); + memset(&outputStruct, 0, sizeof(SMCParamStruct)); + memset(result_smc, 0, sizeof(smc_return_t)); + + // First call to AppleSMC - get key info + inputStruct.key = to_uint32_t(key); + inputStruct.data8 = kSMCGetKeyInfo; + + result = call_smc(&inputStruct, &outputStruct); + result_smc->kSMC = outputStruct.result; + + if (result != kIOReturnSuccess || outputStruct.result != kSMCSuccess) { + return result; + } + + // Store data for return + result_smc->dataSize = outputStruct.keyInfo.dataSize; + result_smc->dataType = outputStruct.keyInfo.dataType; + + + // Second call to AppleSMC - now we can get the data + inputStruct.keyInfo.dataSize = outputStruct.keyInfo.dataSize; + inputStruct.data8 = kSMCReadKey; + + result = call_smc(&inputStruct, &outputStruct); + result_smc->kSMC = outputStruct.result; + + if (result != kIOReturnSuccess || outputStruct.result != kSMCSuccess) { + return result; + } + + memcpy(result_smc->data, outputStruct.bytes, sizeof(outputStruct.bytes)); + + return result; +} + + +/** +Write data to the SMC. + +:returns: IOReturn IOKit return code +*/ +static kern_return_t write_smc(char *key, smc_return_t *result_smc) +{ + kern_return_t result; + SMCParamStruct inputStruct; + SMCParamStruct outputStruct; + + memset(&inputStruct, 0, sizeof(SMCParamStruct)); + memset(&outputStruct, 0, sizeof(SMCParamStruct)); + + // First call to AppleSMC - get key info + inputStruct.key = to_uint32_t(key); + inputStruct.data8 = kSMCGetKeyInfo; + + result = call_smc(&inputStruct, &outputStruct); + result_smc->kSMC = outputStruct.result; + + if (result != kIOReturnSuccess || outputStruct.result != kSMCSuccess) { + return result; + } + + // Check data is correct + if (result_smc->dataSize != outputStruct.keyInfo.dataSize || + result_smc->dataType != outputStruct.keyInfo.dataType) { + return kIOReturnBadArgument; + } + + // Second call to AppleSMC - now we can write the data + inputStruct.data8 = kSMCWriteKey; + inputStruct.keyInfo.dataSize = outputStruct.keyInfo.dataSize; + + // Set data to write + memcpy(inputStruct.bytes, result_smc->data, sizeof(result_smc->data)); + + result = call_smc(&inputStruct, &outputStruct); + result_smc->kSMC = outputStruct.result; + + return result; +} + + +/** +Get the model name of the machine. +*/ +static kern_return_t get_machine_model(io_name_t model) +{ + io_service_t service; + kern_return_t result; + + service = IOServiceGetMatchingService(kIOMasterPortDefault, + IOServiceMatching(IOSERVICE_MODEL)); + + if (service == 0) { + printf("ERROR: %s NOT FOUND\n", IOSERVICE_MODEL); + return kIOReturnError; + } + + // Get the model name + result = IORegistryEntryGetName(service, model); + IOObjectRelease(service); + + return result; +} + + +//------------------------------------------------------------------------------ +// MARK: "PUBLIC" FUNCTIONS +//------------------------------------------------------------------------------ + + +kern_return_t open_smc(void) +{ + kern_return_t result; + io_service_t service; + + service = IOServiceGetMatchingService(kIOMasterPortDefault, + IOServiceMatching(IOSERVICE_SMC)); + + if (service == 0) { + // NOTE: IOServiceMatching documents 0 on failure + printf("ERROR: %s NOT FOUND\n", IOSERVICE_SMC); + return kIOReturnError; + } + + result = IOServiceOpen(service, mach_task_self(), 0, &conn); + IOObjectRelease(service); + + return result; +} + + +kern_return_t close_smc(void) +{ + return IOServiceClose(conn); +} + + +bool is_key_valid(char *key) +{ + bool ans = false; + kern_return_t result; + smc_return_t result_smc; + + if (strlen(key) != SMC_KEY_SIZE) { + printf("ERROR: Invalid key size - must be 4 chars\n"); + return ans; + } + + // Try a read and see if it succeeds + result = read_smc(key, &result_smc); + + if (result == kIOReturnSuccess && result_smc.kSMC == kSMCSuccess) { + ans = true; + } + + return ans; +} + + +double get_tmp(char *key, tmp_unit_t unit) +{ + kern_return_t result; + smc_return_t result_smc; + + result = read_smc(key, &result_smc); + + if (!(result == kIOReturnSuccess && + result_smc.dataSize == 2 && + result_smc.dataType == to_uint32_t(DATA_TYPE_SP78))) { + // Error + return 0.0; + } + + // TODO: Create from_sp78() convert function + double tmp = result_smc.data[0]; + + switch (unit) { + case CELSIUS: + break; + case FAHRENHEIT: + tmp = to_fahrenheit(tmp); + break; + case KELVIN: + tmp = to_kelvin(tmp); + break; + } + + return tmp; +} + + +bool is_battery_powered(void) +{ + kern_return_t result; + smc_return_t result_smc; + + result = read_smc(BATT_PWR, &result_smc); + + if (!(result == kIOReturnSuccess && + result_smc.dataSize == 1 && + result_smc.dataType == to_uint32_t(DATA_TYPE_FLAG))) { + // Error + return false; + } + + return result_smc.data[0]; +} + + +bool is_optical_disk_drive_full(void) +{ + kern_return_t result; + smc_return_t result_smc; + + result = read_smc(ODD_FULL, &result_smc); + + if (!(result == kIOReturnSuccess && + result_smc.dataSize == 1 && + result_smc.dataType == to_uint32_t(DATA_TYPE_FLAG))) { + // Error + return false; + } + + return result_smc.data[0]; +} + + +//------------------------------------------------------------------------------ +// MARK: FAN FUNCTIONS +//------------------------------------------------------------------------------ + + +bool get_fan_name(unsigned int fan_num, fan_name_t name) +{ + char key[5]; + kern_return_t result; + smc_return_t result_smc; + + sprintf(key, "F%dID", fan_num); + result = read_smc(key, &result_smc); + + if (!(result == kIOReturnSuccess && + result_smc.dataSize == 16 && + result_smc.dataType == to_uint32_t(DATA_TYPE_SFDS))) { + return false; + } + + + /* + We know the data size is 16 bytes and the type is "{fds", a custom + struct defined by the AppleSMC.kext. See TMP enum sources for the + struct. + + The last 12 bytes contain the name of the fan, an array of chars, hence + the loop range. + */ + int index = 0; + for (int i = 4; i < 16; i++) { + // Check if at the end (name may not be full 12 bytes) + // Could check for 0 (null), but instead we check for 32 (space). This + // is a hack to remove whitespace. :) + if (result_smc.data[i] == 32) { + break; + } + + name[index] = result_smc.data[i]; + index++; + } + + return true; +} + + +int get_num_fans(void) +{ + kern_return_t result; + smc_return_t result_smc; + + result = read_smc(NUM_FANS, &result_smc); + + if (!(result == kIOReturnSuccess && + result_smc.dataSize == 1 && + result_smc.dataType == to_uint32_t(DATA_TYPE_UINT8))) { + // Error + return -1; + } + + return result_smc.data[0]; +} + + +unsigned int get_fan_rpm(unsigned int fan_num) +{ + char key[5]; + kern_return_t result; + smc_return_t result_smc; + + sprintf(key, "F%dAc", fan_num); + result = read_smc(key, &result_smc); + + if (!(result == kIOReturnSuccess && + result_smc.dataSize == 2 && + result_smc.dataType == to_uint32_t(DATA_TYPE_FPE2))) { + // Error + return 0; + } + + return from_fpe2(result_smc.data); +} + + +bool set_fan_min_rpm(unsigned int fan_num, unsigned int rpm, bool auth) +{ + // TODO: Add rpm val safety check + char key[5]; + bool ans = false; + kern_return_t result; + smc_return_t result_smc; + + memset(&result_smc, 0, sizeof(smc_return_t)); + + // TODO: Don't use magic number + result_smc.dataSize = 2; + result_smc.dataType = to_uint32_t(DATA_TYPE_FPE2); + to_fpe2(rpm, result_smc.data); + + sprintf(key, "F%dMn", fan_num); + result = write_smc(key, &result_smc); + + if (result == kIOReturnSuccess && result_smc.kSMC == kSMCSuccess) { + ans = true; + } + + return ans; +} diff --git a/host/smc.h b/host/smc.h new file mode 100644 index 0000000..b156368 --- /dev/null +++ b/host/smc.h @@ -0,0 +1,254 @@ +/* + * Apple System Management Controller (SMC) API from user space for Intel based + * Macs. Works by talking to the AppleSMC.kext (kernel extension), the driver + * for the SMC. + * + * smc.h + * libsmc + * + * Copyright (C) 2014 beltex + * + * Based off of fork from: + * osx-cpu-temp + * + * With credits to: + * + * Copyright (C) 2006 devnull + * Apple System Management Control (SMC) Tool + * + * Copyright (C) 2006 Hendrik Holtmann + * smcFanControl + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include + + +//------------------------------------------------------------------------------ +// MARK: MACROS +//------------------------------------------------------------------------------ + + +/** +SMC keys for temperature sensors - 4 byte multi-character constants + +Not applicable to all Mac's of course. In adition, the definition of the codes +may not be 100% accurate necessarily. Finally, list is incomplete. + +Presumed letter translations: + +- T = Temperature (if first char) +- C = CPU +- G = GPU +- P = Proximity +- D = Diode +- H = Heatsink + +Sources: + +- https://www.apple.com/downloads/dashboard/status/istatpro.html +- https://github.com/hholtmann/smcFanControl +- https://github.com/jedda/OSX-Monitoring-Tools +- http://www.parhelia.ch/blog/statics/k3_keys.html +*/ +#define AMBIENT_AIR_0 "TA0P" +#define AMBIENT_AIR_1 "TA1P" +#define CPU_0_DIODE "TC0D" +#define CPU_0_HEATSINK "TC0H" +#define CPU_0_PROXIMITY "TC0P" +#define ENCLOSURE_BASE_0 "TB0T" +#define ENCLOSURE_BASE_1 "TB1T" +#define ENCLOSURE_BASE_2 "TB2T" +#define ENCLOSURE_BASE_3 "TB3T" +#define GPU_0_DIODE "TG0D" +#define GPU_0_HEATSINK "TG0H" +#define GPU_0_PROXIMITY "TG0P" +#define HARD_DRIVE_BAY "TH0P" +#define MEMORY_SLOT_0 "TM0S" +#define MEMORY_SLOTS_PROXIMITY "TM0P" +#define NORTHBRIDGE "TN0H" +#define NORTHBRIDGE_DIODE "TN0D" +#define NORTHBRIDGE_PROXIMITY "TN0P" +#define THUNDERBOLT_0 "TI0P" +#define THUNDERBOLT_1 "TI1P" +#define WIRELESS_MODULE "TW0P" + + +/** +SMC keys for fans - 4 byte multi-character constants + +Number of fans on Macs vary of course, thus not all keys will be applicable. + +Presumed letter translations: + +- F = Fan +- Ac = Acutal +- Mn = Min +- Mx = Max +- Sf = Safe +- Tg = Target + +Sources: See TMP SMC keys +*/ +#define FAN_0 "F0Ac" +#define FAN_0_MIN_RPM "F0Mn" +#define FAN_0_MAX_RPM "F0Mx" +#define FAN_0_SAFE_RPM "F0Sf" +#define FAN_0_TARGET_RPM "F0Tg" +#define FAN_1 "F1Ac" +#define FAN_1_MIN_RPM "F1Mn" +#define FAN_1_MAX_RPM "F1Mx" +#define FAN_1_SAFE_RPM "F1Sf" +#define FAN_1_TARGET_RPM "F1Tg" +#define FAN_2 "F2Ac" +#define FAN_2_MIN_RPM "F2Mn" +#define FAN_2_MAX_RPM "F2Mx" +#define FAN_2_SAFE_RPM "F2Sf" +#define FAN_2_TARGET_RPM "F2Tg" +#define NUM_FANS "FNum" +#define FORCE_BITS "FS! " + + +/** +Misc SMC keys - 4 byte multi-character constants + +Sources: See TMP SMC keys +*/ +#define BATT_PWR "BATP" +#define NUM_KEYS "#KEY" +#define ODD_FULL "MSDI" + + +//------------------------------------------------------------------------------ +// MARK: TYPES +//------------------------------------------------------------------------------ + + +typedef char fan_name_t[13]; + + +//------------------------------------------------------------------------------ +// MARK: ENUMS +//------------------------------------------------------------------------------ + + +typedef enum { + CELSIUS, + FAHRENHEIT, + KELVIN +} tmp_unit_t; + + +//------------------------------------------------------------------------------ +// MARK: PROTOTYPES +//------------------------------------------------------------------------------ + + +/** +Open a connection to the SMC + +:returns: kIOReturnSuccess on successful connection to the SMC. +*/ +kern_return_t open_smc(void); + + +/** +Close connection to the SMC + +:returns: kIOReturnSuccess on successful close of connection to the SMC. +*/ +kern_return_t close_smc(void); + + +/** +Check if an SMC key is valid. Useful for determining if a certain machine has +particular sensor or fan for example. + +:param: key The SMC key to check. 4 byte multi-character constant. Must be 4 + characters in length. +:returns: True if the key is found, false otherwise +*/ +bool is_key_valid(char *key); + + +/** +Get the current temperature from a sensor + +:param: key The temperature sensor to read from +:param: unit The unit for the temperature value. +:returns: Temperature of sensor. If the sensor is not found, or an error + occurs, return will be zero +*/ +double get_tmp(char *key, tmp_unit_t unit); + + +/** +Is the machine being powered by the battery? + +:returns: True if it is, false otherwise +*/ +bool is_battery_powered(void); + + +/** +Is there a CD in the optical disk drive (ODD)? + +:returns: True if there is, false otherwise +*/ +bool is_optical_disk_drive_full(void); + + +/** +Get the name of a fan. + +:param: fanNum The number of the fan to check +:param: name The name of the fan. Return will be empty on error. +:returns: True if successful, false otherwise. +*/ +bool get_fan_name(unsigned int fan_num, fan_name_t name); + + +/** +Get the number of fans on this machine. + +:returns: The number of fans. If an error occurs, return will be -1. +*/ +int get_num_fans(void); + + +/** +Get the current speed (RPM - revolutions per minute) of a fan. + +:param: fan_num The number of the fan to check +:returns: The fan RPM. If the fan is not found, or an error occurs, return + will be zero +*/ +UInt get_fan_rpm(UInt fan_num); + + +/** +Set the minimum speed (RPM - revolutions per minute) of a fan. This method +requires root privileges. By minimum we mean that OS X can interject and +raise the fan speed if needed, however it will not go below this. + +WARNING: You are playing with hardware here, BE CAREFUL. + +:param: fan_num The number of the fan to set +:param: rpm The speed you would like to set the fan to. +:param: auth Should the function do authentication? +:return: True if successful, false otherwise +*/ +bool set_fan_min_rpm(unsigned int fan_num, unsigned int rpm, bool auth); From 5b52fbbea53fdce4dbd3edc950af892f86a6a8ae Mon Sep 17 00:00:00 2001 From: Olivier Vielpeau Date: Tue, 26 Dec 2017 16:09:49 +0100 Subject: [PATCH 03/62] [mem] Add commit-related stats to VirtualMemoryStat on Linux --- mem/mem.go | 2 ++ mem/mem_linux.go | 4 ++++ mem/mem_test.go | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/mem/mem.go b/mem/mem.go index 87dfb53..01f3570 100644 --- a/mem/mem.go +++ b/mem/mem.go @@ -58,6 +58,8 @@ type VirtualMemoryStat struct { Slab uint64 `json:"slab"` PageTables uint64 `json:"pagetables"` SwapCached uint64 `json:"swapcached"` + CommitLimit uint64 `json:"commitlimit"` + CommittedAS uint64 `json:"committedas"` } type SwapMemoryStat struct { diff --git a/mem/mem_linux.go b/mem/mem_linux.go index fda3345..71eb654 100644 --- a/mem/mem_linux.go +++ b/mem/mem_linux.go @@ -60,6 +60,10 @@ func VirtualMemory() (*VirtualMemoryStat, error) { ret.PageTables = t * 1024 case "SwapCached": ret.SwapCached = t * 1024 + case "CommitLimit": + ret.CommitLimit = t * 1024 + case "Committed_AS": + ret.CommittedAS = t * 1024 } } if !memavail { diff --git a/mem/mem_test.go b/mem/mem_test.go index 4a1ae7b..feeb59e 100644 --- a/mem/mem_test.go +++ b/mem/mem_test.go @@ -57,7 +57,7 @@ func TestVirtualMemoryStat_String(t *testing.T) { UsedPercent: 30.1, Free: 40, } - e := `{"total":10,"available":20,"used":30,"usedPercent":30.1,"free":40,"active":0,"inactive":0,"wired":0,"buffers":0,"cached":0,"writeback":0,"dirty":0,"writebacktmp":0,"shared":0,"slab":0,"pagetables":0,"swapcached":0}` + e := `{"total":10,"available":20,"used":30,"usedPercent":30.1,"free":40,"active":0,"inactive":0,"wired":0,"buffers":0,"cached":0,"writeback":0,"dirty":0,"writebacktmp":0,"shared":0,"slab":0,"pagetables":0,"swapcached":0,"commitlimit":0,"committedas":0}` if e != fmt.Sprintf("%v", v) { t.Errorf("VirtualMemoryStat string is invalid: %v", v) } From 756e91c8f55c589c277c57041e4a7a504168e346 Mon Sep 17 00:00:00 2001 From: shirou Date: Sun, 31 Dec 2017 17:15:45 +0900 Subject: [PATCH 04/62] [disk]linux: use basename such as sda1 for IOCounters --- disk/disk_linux.go | 6 ++++++ disk/disk_test.go | 3 +++ 2 files changed, 9 insertions(+) diff --git a/disk/disk_linux.go b/disk/disk_linux.go index f31c391..8617288 100644 --- a/disk/disk_linux.go +++ b/disk/disk_linux.go @@ -5,6 +5,7 @@ package disk import ( "fmt" "os/exec" + "path/filepath" "strconv" "strings" @@ -280,6 +281,11 @@ func IOCounters(names ...string) (map[string]IOCountersStat, error) { ret := make(map[string]IOCountersStat, 0) empty := IOCountersStat{} + // use only basename such as "/dev/sda1" to "sda1" + for i, name := range names { + names[i] = filepath.Base(name) + } + for _, line := range lines { fields := strings.Fields(line) if len(fields) < 14 { diff --git a/disk/disk_test.go b/disk/disk_test.go index 436b8a5..269ca6c 100644 --- a/disk/disk_test.go +++ b/disk/disk_test.go @@ -25,6 +25,8 @@ func TestDisk_partitions(t *testing.T) { if err != nil || len(ret) == 0 { t.Errorf("error %v", err) } + t.Log(ret) + empty := PartitionStat{} if len(ret) == 0 { t.Errorf("ret is empty") @@ -46,6 +48,7 @@ func TestDisk_io_counters(t *testing.T) { } empty := IOCountersStat{} for part, io := range ret { + t.Log(part, io) if io == empty { t.Errorf("io_counter error %v, %v", part, io) } From 482ca3af6d842055fe735f0db994896fca1e7505 Mon Sep 17 00:00:00 2001 From: Nick Kirsch Date: Thu, 4 Jan 2018 11:30:39 -0800 Subject: [PATCH 05/62] Parses the tgid field, which is the thread group id (aka user-space process id) on Linux. Returns error on other platforms. --- process/process.go | 2 ++ process/process_fallback.go | 3 +++ process/process_linux.go | 16 ++++++++++++++++ 3 files changed, 21 insertions(+) diff --git a/process/process.go b/process/process.go index df5da2e..0e089bb 100644 --- a/process/process.go +++ b/process/process.go @@ -30,6 +30,8 @@ type Process struct { lastCPUTimes *cpu.TimesStat lastCPUTime time.Time + + tgid int32 } type OpenFilesStat struct { diff --git a/process/process_fallback.go b/process/process_fallback.go index 12bd3a6..e8f545b 100644 --- a/process/process_fallback.go +++ b/process/process_fallback.go @@ -41,6 +41,9 @@ func (p *Process) Ppid() (int32, error) { func (p *Process) Name() (string, error) { return "", common.ErrNotImplementedError } +func (p *Process) Tgid() (int32, error) { + return 0, common.ErrNotImplementedError +} func (p *Process) Exe() (string, error) { return "", common.ErrNotImplementedError } diff --git a/process/process_linux.go b/process/process_linux.go index 9db1997..4b1ace1 100644 --- a/process/process_linux.go +++ b/process/process_linux.go @@ -100,6 +100,16 @@ func (p *Process) Name() (string, error) { return p.name, nil } +// Tgid returns tgid, a Linux-synonym for user-space Pid +func (p *Process) Tgid() (int32, error) { + if p.tgid == 0 { + if err := p.fillFromStatus(); err != nil { + return 0, err + } + } + return p.tgid, nil +} + // Exe returns executable path of the process. func (p *Process) Exe() (string, error) { return p.fillFromExe() @@ -820,6 +830,12 @@ func (p *Process) fillFromStatus() error { return err } p.parent = int32(pval) + case "Tgid": + pval, err := strconv.ParseInt(value, 10, 32) + if err != nil { + return err + } + p.tgid = int32(pval) case "Uid": p.uids = make([]int32, 0, 4) for _, i := range strings.Split(value, "\t") { From 6c35887d024f4257ed09809d3bab3f2fc50fe14f Mon Sep 17 00:00:00 2001 From: Nick Kirsch Date: Thu, 4 Jan 2018 11:51:49 -0800 Subject: [PATCH 06/62] Add ErrNotImplementedError to Darwin, FreeBSD, and OpenBSD. --- process/process_darwin.go | 3 +++ process/process_freebsd.go | 3 +++ process/process_openbsd.go | 3 +++ 3 files changed, 9 insertions(+) diff --git a/process/process_darwin.go b/process/process_darwin.go index cc289d2..2ed7a0b 100644 --- a/process/process_darwin.go +++ b/process/process_darwin.go @@ -83,6 +83,9 @@ func (p *Process) Name() (string, error) { return common.IntToString(k.Proc.P_comm[:]), nil } +func (p *Process) Tgid() (int32, error) { + return 0, common.ErrNotImplementedError +} func (p *Process) Exe() (string, error) { lsof_bin, err := exec.LookPath("lsof") if err != nil { diff --git a/process/process_freebsd.go b/process/process_freebsd.go index 9d75717..4db021d 100644 --- a/process/process_freebsd.go +++ b/process/process_freebsd.go @@ -50,6 +50,9 @@ func (p *Process) Name() (string, error) { return common.IntToString(k.Comm[:]), nil } +func (p *Process) Tgid() (int32, error) { + return 0, common.ErrNotImplementedError +} func (p *Process) Exe() (string, error) { return "", common.ErrNotImplementedError } diff --git a/process/process_openbsd.go b/process/process_openbsd.go index aca4ffa..088abef 100644 --- a/process/process_openbsd.go +++ b/process/process_openbsd.go @@ -53,6 +53,9 @@ func (p *Process) Name() (string, error) { return common.IntToString(k.Comm[:]), nil } +func (p *Process) Tgid() (int32, error) { + return 0, common.ErrNotImplementedError +} func (p *Process) Exe() (string, error) { return "", common.ErrNotImplementedError } From fb24c70d366b88caa381823e8e7e387784fcda8b Mon Sep 17 00:00:00 2001 From: Nick Kirsch Date: Fri, 5 Jan 2018 11:37:36 -0800 Subject: [PATCH 07/62] Add ErrNotImplementedError for Tgid support. --- process/process_windows.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/process/process_windows.go b/process/process_windows.go index 3ce840b..14d466e 100644 --- a/process/process_windows.go +++ b/process/process_windows.go @@ -154,6 +154,10 @@ func (p *Process) Name() (string, error) { return name, nil } +func (p *Process) Tgid() (int32, error) { + return 0, common.ErrNotImplementedError +} + func (p *Process) Exe() (string, error) { dst, err := GetWin32Proc(p.Pid) if err != nil { From 231815dfeab1ddac1419fcaf564eea078fdf9bfc Mon Sep 17 00:00:00 2001 From: shirou Date: Tue, 9 Jan 2018 11:12:00 +0900 Subject: [PATCH 08/62] [docker]: move String() to docker.go for all platforms. --- docker/docker.go | 11 +++++++++++ docker/docker_linux.go | 11 ----------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docker/docker.go b/docker/docker.go index 1d932cf..83716fd 100644 --- a/docker/docker.go +++ b/docker/docker.go @@ -1,6 +1,7 @@ package docker import ( + "encoding/json" "errors" "github.com/shirou/gopsutil/internal/common" @@ -50,6 +51,11 @@ type CgroupMemStat struct { MemFailCnt uint64 `json:"memoryFailcnt"` } +func (m CgroupMemStat) String() string { + s, _ := json.Marshal(m) + return string(s) +} + type CgroupDockerStat struct { ContainerID string `json:"containerID"` Name string `json:"name"` @@ -57,3 +63,8 @@ type CgroupDockerStat struct { Status string `json:"status"` Running bool `json:"running"` } + +func (c CgroupDockerStat) String() string { + s, _ := json.Marshal(c) + return string(s) +} diff --git a/docker/docker_linux.go b/docker/docker_linux.go index 000d2f2..7cc56d8 100644 --- a/docker/docker_linux.go +++ b/docker/docker_linux.go @@ -3,7 +3,6 @@ package docker import ( - "encoding/json" "fmt" "os" "os/exec" @@ -52,11 +51,6 @@ func GetDockerStat() ([]CgroupDockerStat, error) { return ret, nil } -func (c CgroupDockerStat) String() string { - s, _ := json.Marshal(c) - return string(s) -} - // GetDockerIDList returnes a list of DockerID. // This requires certain permission. func GetDockerIDList() ([]string, error) { @@ -220,11 +214,6 @@ func CgroupMemDocker(containerID string) (*CgroupMemStat, error) { return CgroupMem(containerID, common.HostSys("fs/cgroup/memory/docker")) } -func (m CgroupMemStat) String() string { - s, _ := json.Marshal(m) - return string(s) -} - // getCgroupFilePath constructs file path to get targetted stats file. func getCgroupFilePath(containerID, base, target, file string) string { if len(base) == 0 { From 1958326a49959d5b31e3a86483c12b394da1950e Mon Sep 17 00:00:00 2001 From: shirou Date: Tue, 9 Jan 2018 11:21:13 +0900 Subject: [PATCH 09/62] [docker]: fix re-declartion --- docker/docker_notlinux.go | 7 ------- 1 file changed, 7 deletions(-) diff --git a/docker/docker_notlinux.go b/docker/docker_notlinux.go index f78fb33..e403dbb 100644 --- a/docker/docker_notlinux.go +++ b/docker/docker_notlinux.go @@ -3,8 +3,6 @@ package docker import ( - "encoding/json" - "github.com/shirou/gopsutil/cpu" "github.com/shirou/gopsutil/internal/common" ) @@ -40,8 +38,3 @@ func CgroupMem(containerid string, base string) (*CgroupMemStat, error) { func CgroupMemDocker(containerid string) (*CgroupMemStat, error) { return CgroupMem(containerid, common.HostSys("fs/cgroup/memory/docker")) } - -func (m CgroupMemStat) String() string { - s, _ := json.Marshal(m) - return string(s) -} From d1fb05a30bd0d38b2e56ca3cdf04b0cd30ba2083 Mon Sep 17 00:00:00 2001 From: shirou Date: Tue, 9 Jan 2018 11:30:38 +0900 Subject: [PATCH 10/62] Skip some test if CircleCI. --- host/host_test.go | 8 ++++++++ process/process_test.go | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/host/host_test.go b/host/host_test.go index 87a6bd9..b4f3e40 100644 --- a/host/host_test.go +++ b/host/host_test.go @@ -2,6 +2,7 @@ package host import ( "fmt" + "os" "testing" ) @@ -20,6 +21,10 @@ func TestHostInfo(t *testing.T) { } func TestUptime(t *testing.T) { + if os.Getenv("CIRCLECI") == "true" { + t.Skip("Skip CI") + } + v, err := Uptime() if err != nil { t.Errorf("error %v", err) @@ -30,6 +35,9 @@ func TestUptime(t *testing.T) { } func TestBoot_time(t *testing.T) { + if os.Getenv("CIRCLECI") == "true" { + t.Skip("Skip CI") + } v, err := BootTime() if err != nil { t.Errorf("error %v", err) diff --git a/process/process_test.go b/process/process_test.go index 84c24bd..d8eb483 100644 --- a/process/process_test.go +++ b/process/process_test.go @@ -301,6 +301,10 @@ func Test_Process_CpuPercentLoop(t *testing.T) { } func Test_Process_CreateTime(t *testing.T) { + if os.Getenv("CIRCLECI") == "true" { + t.Skip("Skip CI") + } + p := testGetProcess() c, err := p.CreateTime() From e776f6384a7fa7bb3aad41bb9afdffb02bcdfb31 Mon Sep 17 00:00:00 2001 From: shirou Date: Tue, 9 Jan 2018 11:44:33 +0900 Subject: [PATCH 11/62] [docker]: fix typo. --- docker/docker_linux.go | 1 - 1 file changed, 1 deletion(-) diff --git a/docker/docker_linux.go b/docker/docker_linux.go index c0724db..ef6cae0 100644 --- a/docker/docker_linux.go +++ b/docker/docker_linux.go @@ -4,7 +4,6 @@ package docker import ( "context" - "encoding/json" "fmt" "os" "os/exec" From 079e1cb22f1dbe4fa44b93c95fdd5edf8ac7bebd Mon Sep 17 00:00:00 2001 From: shirou Date: Thu, 11 Jan 2018 11:34:14 +0900 Subject: [PATCH 12/62] [internal]Windows: skip Host test. --- internal/common/common_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/common/common_test.go b/internal/common/common_test.go index 3b05d53..cd33388 100644 --- a/internal/common/common_test.go +++ b/internal/common/common_test.go @@ -4,6 +4,7 @@ import ( "fmt" "os" "reflect" + "runtime" "strings" "testing" ) @@ -92,6 +93,9 @@ func TestPathExists(t *testing.T) { } func TestHostEtc(t *testing.T) { + if runtime.GOOS == "windows" { + t.Skip("windows doesn't have etc") + } p := HostEtc("mtab") if p != "/etc/mtab" { t.Errorf("invalid HostEtc, %s", p) From 6a368fb7cd1221fa6ea90facc9447c9a2234c255 Mon Sep 17 00:00:00 2001 From: shirou Date: Thu, 11 Jan 2018 11:47:13 +0900 Subject: [PATCH 13/62] [process]: add ProcessesWithContext. --- process/process_darwin.go | 4 ++++ process/process_freebsd.go | 4 ++++ process/process_linux.go | 4 ++++ process/process_openbsd.go | 4 ++++ process/process_windows.go | 4 ++++ 5 files changed, 20 insertions(+) diff --git a/process/process_darwin.go b/process/process_darwin.go index 97d1c49..588640a 100644 --- a/process/process_darwin.go +++ b/process/process_darwin.go @@ -523,6 +523,10 @@ func (p *Process) MemoryMapsWithContext(ctx context.Context, grouped bool) (*[]M } func Processes() ([]*Process, error) { + return ProcessesWithContext(context.Background()) +} + +func ProcessesWithContext(ctx context.Context) ([]*Process, error) { results := []*Process{} mib := []int32{CTLKern, KernProc, KernProcAll, 0} diff --git a/process/process_freebsd.go b/process/process_freebsd.go index 29a223a..13b3d88 100644 --- a/process/process_freebsd.go +++ b/process/process_freebsd.go @@ -411,6 +411,10 @@ func (p *Process) MemoryMapsWithContext(ctx context.Context, grouped bool) (*[]M } func Processes() ([]*Process, error) { + return ProcessesWithContext(context.Background()) +} + +func ProcessesWithContext(ctx context.Context) ([]*Process, error) { results := []*Process{} mib := []int32{CTLKern, KernProc, KernProcProc, 0} diff --git a/process/process_linux.go b/process/process_linux.go index 14d151b..7a5ebb0 100644 --- a/process/process_linux.go +++ b/process/process_linux.go @@ -1213,6 +1213,10 @@ func PidsWithContext(ctx context.Context) ([]int32, error) { // Process returns a slice of pointers to Process structs for all // currently running processes. func Processes() ([]*Process, error) { + return ProcessesWithContext(context.Background()) +} + +func ProcessesWithContext(ctx context.Context) ([]*Process, error) { out := []*Process{} pids, err := Pids() diff --git a/process/process_openbsd.go b/process/process_openbsd.go index 857bad0..78631ac 100644 --- a/process/process_openbsd.go +++ b/process/process_openbsd.go @@ -401,6 +401,10 @@ func (p *Process) MemoryMapsWithContext(ctx context.Context, grouped bool) (*[]M } func Processes() ([]*Process, error) { + return ProcessesWithContext(context.Background()) +} + +func ProcessesWithContext(ctx context.Context) ([]*Process, error) { results := []*Process{} buf, length, err := CallKernProcSyscall(KernProcAll, 0) diff --git a/process/process_windows.go b/process/process_windows.go index f1fa1cb..d35fcc2 100644 --- a/process/process_windows.go +++ b/process/process_windows.go @@ -600,6 +600,10 @@ func getFromSnapProcess(pid int32) (int32, int32, string, error) { // Get processes func Processes() ([]*Process, error) { + return ProcessesWithContext(context.Background()) +} + +func ProcessesWithContext(ctx context.Context) ([]*Process, error) { pids, err := Pids() if err != nil { return []*Process{}, fmt.Errorf("could not get Processes %s", err) From a271cb7c1c6caa3e2e6938b7e83e2fffe710bead Mon Sep 17 00:00:00 2001 From: Tatiana Borisova Date: Thu, 25 Jan 2018 17:39:00 +0000 Subject: [PATCH 14/62] Fix package name in host_cgo.go --- host/host_cgo.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/host/host_cgo.go b/host/host_cgo.go index c95dd23..c4a5995 100644 --- a/host/host_cgo.go +++ b/host/host_cgo.go @@ -1,3 +1,3 @@ // +build cgo -package gopsutil +package host From 945c2f41de0e9b29c542ea45dfcdf08618670985 Mon Sep 17 00:00:00 2001 From: Tatiana Borisova Date: Thu, 25 Jan 2018 18:25:11 +0000 Subject: [PATCH 15/62] Fix host_cgo file --- host/host_cgo.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/host/host_cgo.go b/host/host_cgo.go index c4a5995..50a063e 100644 --- a/host/host_cgo.go +++ b/host/host_cgo.go @@ -1,3 +1,5 @@ // +build cgo package host + +import "C" From 4ac6b5b4d3d1a18a341018df3054f617bb6b89d4 Mon Sep 17 00:00:00 2001 From: shirou Date: Sat, 27 Jan 2018 00:58:16 +0900 Subject: [PATCH 16/62] Revert "Merge pull request #470 from improbable-io/bug-darwin-build-2" This reverts commit bb09b4e7d97d450a7836ae174cf812bbdbfe5e9d, reversing changes made to 6a368fb7cd1221fa6ea90facc9447c9a2234c255. --- host/host_cgo.go | 5 - host/host_darwin_cgo.go | 2 +- host/include/smc.c | 700 ++++++++++++++++++++++++++++++++++++++++++++++++ host/include/smc.h | 254 ++++++++++++++++++ host/smc.c | 700 ------------------------------------------------ host/smc.h | 254 ------------------ 6 files changed, 955 insertions(+), 960 deletions(-) delete mode 100644 host/host_cgo.go create mode 100644 host/include/smc.c create mode 100644 host/include/smc.h delete mode 100644 host/smc.c delete mode 100644 host/smc.h diff --git a/host/host_cgo.go b/host/host_cgo.go deleted file mode 100644 index 50a063e..0000000 --- a/host/host_cgo.go +++ /dev/null @@ -1,5 +0,0 @@ -// +build cgo - -package host - -import "C" diff --git a/host/host_darwin_cgo.go b/host/host_darwin_cgo.go index 3151888..f0a4370 100644 --- a/host/host_darwin_cgo.go +++ b/host/host_darwin_cgo.go @@ -4,7 +4,7 @@ package host // #cgo LDFLAGS: -framework IOKit -// #include "smc.h" +// #include "include/smc.c" import "C" import "context" diff --git a/host/include/smc.c b/host/include/smc.c new file mode 100644 index 0000000..30a232b --- /dev/null +++ b/host/include/smc.c @@ -0,0 +1,700 @@ +/* + * Apple System Management Controller (SMC) API from user space for Intel based + * Macs. Works by talking to the AppleSMC.kext (kernel extension), the driver + * for the SMC. + * + * smc.c + * libsmc + * + * Copyright (C) 2014 beltex + * + * Based off of fork from: + * osx-cpu-temp + * + * With credits to: + * + * Copyright (C) 2006 devnull + * Apple System Management Control (SMC) Tool + * + * Copyright (C) 2006 Hendrik Holtmann + * smcFanControl + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include "smc.h" + + +//------------------------------------------------------------------------------ +// MARK: MACROS +//------------------------------------------------------------------------------ + + +/** +Name of the SMC IOService as seen in the IORegistry. You can view it either via +command line with ioreg or through the IORegistryExplorer app (found on Apple's +developer site - Hardware IO Tools for Xcode) +*/ +#define IOSERVICE_SMC "AppleSMC" + + +/** +IOService for getting machine model name +*/ +#define IOSERVICE_MODEL "IOPlatformExpertDevice" + + +/** +SMC data types - 4 byte multi-character constants + +Sources: See TMP SMC keys in smc.h + +http://stackoverflow.com/questions/22160746/fpe2-and-sp78-data-types +*/ +#define DATA_TYPE_UINT8 "ui8 " +#define DATA_TYPE_UINT16 "ui16" +#define DATA_TYPE_UINT32 "ui32" +#define DATA_TYPE_FLAG "flag" +#define DATA_TYPE_FPE2 "fpe2" +#define DATA_TYPE_SFDS "{fds" +#define DATA_TYPE_SP78 "sp78" + + +//------------------------------------------------------------------------------ +// MARK: GLOBAL VARS +//------------------------------------------------------------------------------ + + +/** +Our connection to the SMC +*/ +static io_connect_t conn; + + +/** +Number of characters in an SMC key +*/ +static const int SMC_KEY_SIZE = 4; + + +/** +Number of characters in a data type "key" returned from the SMC. See data type +macros. +*/ +static const int DATA_TYPE_SIZE = 4; + + +//------------------------------------------------------------------------------ +// MARK: ENUMS +//------------------------------------------------------------------------------ + + +/** +Defined by AppleSMC.kext. See SMCParamStruct. + +These are SMC specific return codes +*/ +typedef enum { + kSMCSuccess = 0, + kSMCError = 1, + kSMCKeyNotFound = 0x84 +} kSMC_t; + + +/** +Defined by AppleSMC.kext. See SMCParamStruct. + +Function selectors. Used to tell the SMC which function inside it to call. +*/ +typedef enum { + kSMCUserClientOpen = 0, + kSMCUserClientClose = 1, + kSMCHandleYPCEvent = 2, + kSMCReadKey = 5, + kSMCWriteKey = 6, + kSMCGetKeyCount = 7, + kSMCGetKeyFromIndex = 8, + kSMCGetKeyInfo = 9 +} selector_t; + + +//------------------------------------------------------------------------------ +// MARK: STRUCTS +//------------------------------------------------------------------------------ + + +/** +Defined by AppleSMC.kext. See SMCParamStruct. +*/ +typedef struct { + unsigned char major; + unsigned char minor; + unsigned char build; + unsigned char reserved; + unsigned short release; +} SMCVersion; + + +/** +Defined by AppleSMC.kext. See SMCParamStruct. +*/ +typedef struct { + uint16_t version; + uint16_t length; + uint32_t cpuPLimit; + uint32_t gpuPLimit; + uint32_t memPLimit; +} SMCPLimitData; + + +/** +Defined by AppleSMC.kext. See SMCParamStruct. + +- dataSize : How many values written to SMCParamStruct.bytes +- dataType : Type of data written to SMCParamStruct.bytes. This lets us know how + to interpret it (translate it to human readable) +*/ +typedef struct { + IOByteCount dataSize; + uint32_t dataType; + uint8_t dataAttributes; +} SMCKeyInfoData; + + +/** +Defined by AppleSMC.kext. + +This is the predefined struct that must be passed to communicate with the +AppleSMC driver. While the driver is closed source, the definition of this +struct happened to appear in the Apple PowerManagement project at around +version 211, and soon after disappeared. It can be seen in the PrivateLib.c +file under pmconfigd. + +https://www.opensource.apple.com/source/PowerManagement/PowerManagement-211/ +*/ +typedef struct { + uint32_t key; + SMCVersion vers; + SMCPLimitData pLimitData; + SMCKeyInfoData keyInfo; + uint8_t result; + uint8_t status; + uint8_t data8; + uint32_t data32; + uint8_t bytes[32]; +} SMCParamStruct; + + +/** +Used for returning data from the SMC. +*/ +typedef struct { + uint8_t data[32]; + uint32_t dataType; + uint32_t dataSize; + kSMC_t kSMC; +} smc_return_t; + + +//------------------------------------------------------------------------------ +// MARK: HELPERS - TYPE CONVERSION +//------------------------------------------------------------------------------ + + +/** +Convert data from SMC of fpe2 type to human readable. + +:param: data Data from the SMC to be converted. Assumed data size of 2. +:returns: Converted data +*/ +static unsigned int from_fpe2(uint8_t data[32]) +{ + unsigned int ans = 0; + + // Data type for fan calls - fpe2 + // This is assumend to mean floating point, with 2 exponent bits + // http://stackoverflow.com/questions/22160746/fpe2-and-sp78-data-types + ans += data[0] << 6; + ans += data[1] << 2; + + return ans; +} + + +/** +Convert to fpe2 data type to be passed to SMC. + +:param: val Value to convert +:param: data Pointer to data array to place result +*/ +static void to_fpe2(unsigned int val, uint8_t *data) +{ + data[0] = val >> 6; + data[1] = (val << 2) ^ (data[0] << 8); +} + + +/** +Convert SMC key to uint32_t. This must be done to pass it to the SMC. + +:param: key The SMC key to convert +:returns: uint32_t translation. + Returns zero if key is not 4 characters in length. +*/ +static uint32_t to_uint32_t(char *key) +{ + uint32_t ans = 0; + uint32_t shift = 24; + + // SMC key is expected to be 4 bytes - thus 4 chars + if (strlen(key) != SMC_KEY_SIZE) { + return 0; + } + + for (int i = 0; i < SMC_KEY_SIZE; i++) { + ans += key[i] << shift; + shift -= 8; + } + + return ans; +} + + +/** +For converting the dataType return from the SMC to human readable 4 byte +multi-character constant. +*/ +static void to_string(uint32_t val, char *dataType) +{ + int shift = 24; + + for (int i = 0; i < DATA_TYPE_SIZE; i++) { + // To get each char, we shift it into the lower 8 bits, and then & by + // 255 to insolate it + dataType[i] = (val >> shift) & 0xff; + shift -= 8; + } +} + + +//------------------------------------------------------------------------------ +// MARK: HELPERS - TMP CONVERSION +//------------------------------------------------------------------------------ + + +/** +Celsius to Fahrenheit +*/ +static double to_fahrenheit(double tmp) +{ + // http://en.wikipedia.org/wiki/Fahrenheit#Definition_and_conversions + return (tmp * 1.8) + 32; +} + + +/** +Celsius to Kelvin +*/ +static double to_kelvin(double tmp) +{ + // http://en.wikipedia.org/wiki/Kelvin + return tmp + 273.15; +} + + +//------------------------------------------------------------------------------ +// MARK: "PRIVATE" FUNCTIONS +//------------------------------------------------------------------------------ + + +/** +Make a call to the SMC + +:param: inputStruct Struct that holds data telling the SMC what you want +:param: outputStruct Struct holding the SMC's response +:returns: I/O Kit return code +*/ +static kern_return_t call_smc(SMCParamStruct *inputStruct, + SMCParamStruct *outputStruct) +{ + kern_return_t result; + size_t inputStructCnt = sizeof(SMCParamStruct); + size_t outputStructCnt = sizeof(SMCParamStruct); + + result = IOConnectCallStructMethod(conn, kSMCHandleYPCEvent, + inputStruct, + inputStructCnt, + outputStruct, + &outputStructCnt); + + if (result != kIOReturnSuccess) { + // IOReturn error code lookup. See "Accessing Hardware From Applications + // -> Handling Errors" Apple doc + result = err_get_code(result); + } + + return result; +} + + +/** +Read data from the SMC + +:param: key The SMC key +*/ +static kern_return_t read_smc(char *key, smc_return_t *result_smc) +{ + kern_return_t result; + SMCParamStruct inputStruct; + SMCParamStruct outputStruct; + + memset(&inputStruct, 0, sizeof(SMCParamStruct)); + memset(&outputStruct, 0, sizeof(SMCParamStruct)); + memset(result_smc, 0, sizeof(smc_return_t)); + + // First call to AppleSMC - get key info + inputStruct.key = to_uint32_t(key); + inputStruct.data8 = kSMCGetKeyInfo; + + result = call_smc(&inputStruct, &outputStruct); + result_smc->kSMC = outputStruct.result; + + if (result != kIOReturnSuccess || outputStruct.result != kSMCSuccess) { + return result; + } + + // Store data for return + result_smc->dataSize = outputStruct.keyInfo.dataSize; + result_smc->dataType = outputStruct.keyInfo.dataType; + + + // Second call to AppleSMC - now we can get the data + inputStruct.keyInfo.dataSize = outputStruct.keyInfo.dataSize; + inputStruct.data8 = kSMCReadKey; + + result = call_smc(&inputStruct, &outputStruct); + result_smc->kSMC = outputStruct.result; + + if (result != kIOReturnSuccess || outputStruct.result != kSMCSuccess) { + return result; + } + + memcpy(result_smc->data, outputStruct.bytes, sizeof(outputStruct.bytes)); + + return result; +} + + +/** +Write data to the SMC. + +:returns: IOReturn IOKit return code +*/ +static kern_return_t write_smc(char *key, smc_return_t *result_smc) +{ + kern_return_t result; + SMCParamStruct inputStruct; + SMCParamStruct outputStruct; + + memset(&inputStruct, 0, sizeof(SMCParamStruct)); + memset(&outputStruct, 0, sizeof(SMCParamStruct)); + + // First call to AppleSMC - get key info + inputStruct.key = to_uint32_t(key); + inputStruct.data8 = kSMCGetKeyInfo; + + result = call_smc(&inputStruct, &outputStruct); + result_smc->kSMC = outputStruct.result; + + if (result != kIOReturnSuccess || outputStruct.result != kSMCSuccess) { + return result; + } + + // Check data is correct + if (result_smc->dataSize != outputStruct.keyInfo.dataSize || + result_smc->dataType != outputStruct.keyInfo.dataType) { + return kIOReturnBadArgument; + } + + // Second call to AppleSMC - now we can write the data + inputStruct.data8 = kSMCWriteKey; + inputStruct.keyInfo.dataSize = outputStruct.keyInfo.dataSize; + + // Set data to write + memcpy(inputStruct.bytes, result_smc->data, sizeof(result_smc->data)); + + result = call_smc(&inputStruct, &outputStruct); + result_smc->kSMC = outputStruct.result; + + return result; +} + + +/** +Get the model name of the machine. +*/ +static kern_return_t get_machine_model(io_name_t model) +{ + io_service_t service; + kern_return_t result; + + service = IOServiceGetMatchingService(kIOMasterPortDefault, + IOServiceMatching(IOSERVICE_MODEL)); + + if (service == 0) { + printf("ERROR: %s NOT FOUND\n", IOSERVICE_MODEL); + return kIOReturnError; + } + + // Get the model name + result = IORegistryEntryGetName(service, model); + IOObjectRelease(service); + + return result; +} + + +//------------------------------------------------------------------------------ +// MARK: "PUBLIC" FUNCTIONS +//------------------------------------------------------------------------------ + + +kern_return_t open_smc(void) +{ + kern_return_t result; + io_service_t service; + + service = IOServiceGetMatchingService(kIOMasterPortDefault, + IOServiceMatching(IOSERVICE_SMC)); + + if (service == 0) { + // NOTE: IOServiceMatching documents 0 on failure + printf("ERROR: %s NOT FOUND\n", IOSERVICE_SMC); + return kIOReturnError; + } + + result = IOServiceOpen(service, mach_task_self(), 0, &conn); + IOObjectRelease(service); + + return result; +} + + +kern_return_t close_smc(void) +{ + return IOServiceClose(conn); +} + + +bool is_key_valid(char *key) +{ + bool ans = false; + kern_return_t result; + smc_return_t result_smc; + + if (strlen(key) != SMC_KEY_SIZE) { + printf("ERROR: Invalid key size - must be 4 chars\n"); + return ans; + } + + // Try a read and see if it succeeds + result = read_smc(key, &result_smc); + + if (result == kIOReturnSuccess && result_smc.kSMC == kSMCSuccess) { + ans = true; + } + + return ans; +} + + +double get_tmp(char *key, tmp_unit_t unit) +{ + kern_return_t result; + smc_return_t result_smc; + + result = read_smc(key, &result_smc); + + if (!(result == kIOReturnSuccess && + result_smc.dataSize == 2 && + result_smc.dataType == to_uint32_t(DATA_TYPE_SP78))) { + // Error + return 0.0; + } + + // TODO: Create from_sp78() convert function + double tmp = result_smc.data[0]; + + switch (unit) { + case CELSIUS: + break; + case FAHRENHEIT: + tmp = to_fahrenheit(tmp); + break; + case KELVIN: + tmp = to_kelvin(tmp); + break; + } + + return tmp; +} + + +bool is_battery_powered(void) +{ + kern_return_t result; + smc_return_t result_smc; + + result = read_smc(BATT_PWR, &result_smc); + + if (!(result == kIOReturnSuccess && + result_smc.dataSize == 1 && + result_smc.dataType == to_uint32_t(DATA_TYPE_FLAG))) { + // Error + return false; + } + + return result_smc.data[0]; +} + + +bool is_optical_disk_drive_full(void) +{ + kern_return_t result; + smc_return_t result_smc; + + result = read_smc(ODD_FULL, &result_smc); + + if (!(result == kIOReturnSuccess && + result_smc.dataSize == 1 && + result_smc.dataType == to_uint32_t(DATA_TYPE_FLAG))) { + // Error + return false; + } + + return result_smc.data[0]; +} + + +//------------------------------------------------------------------------------ +// MARK: FAN FUNCTIONS +//------------------------------------------------------------------------------ + + +bool get_fan_name(unsigned int fan_num, fan_name_t name) +{ + char key[5]; + kern_return_t result; + smc_return_t result_smc; + + sprintf(key, "F%dID", fan_num); + result = read_smc(key, &result_smc); + + if (!(result == kIOReturnSuccess && + result_smc.dataSize == 16 && + result_smc.dataType == to_uint32_t(DATA_TYPE_SFDS))) { + return false; + } + + + /* + We know the data size is 16 bytes and the type is "{fds", a custom + struct defined by the AppleSMC.kext. See TMP enum sources for the + struct. + + The last 12 bytes contain the name of the fan, an array of chars, hence + the loop range. + */ + int index = 0; + for (int i = 4; i < 16; i++) { + // Check if at the end (name may not be full 12 bytes) + // Could check for 0 (null), but instead we check for 32 (space). This + // is a hack to remove whitespace. :) + if (result_smc.data[i] == 32) { + break; + } + + name[index] = result_smc.data[i]; + index++; + } + + return true; +} + + +int get_num_fans(void) +{ + kern_return_t result; + smc_return_t result_smc; + + result = read_smc(NUM_FANS, &result_smc); + + if (!(result == kIOReturnSuccess && + result_smc.dataSize == 1 && + result_smc.dataType == to_uint32_t(DATA_TYPE_UINT8))) { + // Error + return -1; + } + + return result_smc.data[0]; +} + + +unsigned int get_fan_rpm(unsigned int fan_num) +{ + char key[5]; + kern_return_t result; + smc_return_t result_smc; + + sprintf(key, "F%dAc", fan_num); + result = read_smc(key, &result_smc); + + if (!(result == kIOReturnSuccess && + result_smc.dataSize == 2 && + result_smc.dataType == to_uint32_t(DATA_TYPE_FPE2))) { + // Error + return 0; + } + + return from_fpe2(result_smc.data); +} + + +bool set_fan_min_rpm(unsigned int fan_num, unsigned int rpm, bool auth) +{ + // TODO: Add rpm val safety check + char key[5]; + bool ans = false; + kern_return_t result; + smc_return_t result_smc; + + memset(&result_smc, 0, sizeof(smc_return_t)); + + // TODO: Don't use magic number + result_smc.dataSize = 2; + result_smc.dataType = to_uint32_t(DATA_TYPE_FPE2); + to_fpe2(rpm, result_smc.data); + + sprintf(key, "F%dMn", fan_num); + result = write_smc(key, &result_smc); + + if (result == kIOReturnSuccess && result_smc.kSMC == kSMCSuccess) { + ans = true; + } + + return ans; +} diff --git a/host/include/smc.h b/host/include/smc.h new file mode 100644 index 0000000..b156368 --- /dev/null +++ b/host/include/smc.h @@ -0,0 +1,254 @@ +/* + * Apple System Management Controller (SMC) API from user space for Intel based + * Macs. Works by talking to the AppleSMC.kext (kernel extension), the driver + * for the SMC. + * + * smc.h + * libsmc + * + * Copyright (C) 2014 beltex + * + * Based off of fork from: + * osx-cpu-temp + * + * With credits to: + * + * Copyright (C) 2006 devnull + * Apple System Management Control (SMC) Tool + * + * Copyright (C) 2006 Hendrik Holtmann + * smcFanControl + * + * This program 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 2 of the License, or + * (at your option) any later version. + * + * This program 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, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include + + +//------------------------------------------------------------------------------ +// MARK: MACROS +//------------------------------------------------------------------------------ + + +/** +SMC keys for temperature sensors - 4 byte multi-character constants + +Not applicable to all Mac's of course. In adition, the definition of the codes +may not be 100% accurate necessarily. Finally, list is incomplete. + +Presumed letter translations: + +- T = Temperature (if first char) +- C = CPU +- G = GPU +- P = Proximity +- D = Diode +- H = Heatsink + +Sources: + +- https://www.apple.com/downloads/dashboard/status/istatpro.html +- https://github.com/hholtmann/smcFanControl +- https://github.com/jedda/OSX-Monitoring-Tools +- http://www.parhelia.ch/blog/statics/k3_keys.html +*/ +#define AMBIENT_AIR_0 "TA0P" +#define AMBIENT_AIR_1 "TA1P" +#define CPU_0_DIODE "TC0D" +#define CPU_0_HEATSINK "TC0H" +#define CPU_0_PROXIMITY "TC0P" +#define ENCLOSURE_BASE_0 "TB0T" +#define ENCLOSURE_BASE_1 "TB1T" +#define ENCLOSURE_BASE_2 "TB2T" +#define ENCLOSURE_BASE_3 "TB3T" +#define GPU_0_DIODE "TG0D" +#define GPU_0_HEATSINK "TG0H" +#define GPU_0_PROXIMITY "TG0P" +#define HARD_DRIVE_BAY "TH0P" +#define MEMORY_SLOT_0 "TM0S" +#define MEMORY_SLOTS_PROXIMITY "TM0P" +#define NORTHBRIDGE "TN0H" +#define NORTHBRIDGE_DIODE "TN0D" +#define NORTHBRIDGE_PROXIMITY "TN0P" +#define THUNDERBOLT_0 "TI0P" +#define THUNDERBOLT_1 "TI1P" +#define WIRELESS_MODULE "TW0P" + + +/** +SMC keys for fans - 4 byte multi-character constants + +Number of fans on Macs vary of course, thus not all keys will be applicable. + +Presumed letter translations: + +- F = Fan +- Ac = Acutal +- Mn = Min +- Mx = Max +- Sf = Safe +- Tg = Target + +Sources: See TMP SMC keys +*/ +#define FAN_0 "F0Ac" +#define FAN_0_MIN_RPM "F0Mn" +#define FAN_0_MAX_RPM "F0Mx" +#define FAN_0_SAFE_RPM "F0Sf" +#define FAN_0_TARGET_RPM "F0Tg" +#define FAN_1 "F1Ac" +#define FAN_1_MIN_RPM "F1Mn" +#define FAN_1_MAX_RPM "F1Mx" +#define FAN_1_SAFE_RPM "F1Sf" +#define FAN_1_TARGET_RPM "F1Tg" +#define FAN_2 "F2Ac" +#define FAN_2_MIN_RPM "F2Mn" +#define FAN_2_MAX_RPM "F2Mx" +#define FAN_2_SAFE_RPM "F2Sf" +#define FAN_2_TARGET_RPM "F2Tg" +#define NUM_FANS "FNum" +#define FORCE_BITS "FS! " + + +/** +Misc SMC keys - 4 byte multi-character constants + +Sources: See TMP SMC keys +*/ +#define BATT_PWR "BATP" +#define NUM_KEYS "#KEY" +#define ODD_FULL "MSDI" + + +//------------------------------------------------------------------------------ +// MARK: TYPES +//------------------------------------------------------------------------------ + + +typedef char fan_name_t[13]; + + +//------------------------------------------------------------------------------ +// MARK: ENUMS +//------------------------------------------------------------------------------ + + +typedef enum { + CELSIUS, + FAHRENHEIT, + KELVIN +} tmp_unit_t; + + +//------------------------------------------------------------------------------ +// MARK: PROTOTYPES +//------------------------------------------------------------------------------ + + +/** +Open a connection to the SMC + +:returns: kIOReturnSuccess on successful connection to the SMC. +*/ +kern_return_t open_smc(void); + + +/** +Close connection to the SMC + +:returns: kIOReturnSuccess on successful close of connection to the SMC. +*/ +kern_return_t close_smc(void); + + +/** +Check if an SMC key is valid. Useful for determining if a certain machine has +particular sensor or fan for example. + +:param: key The SMC key to check. 4 byte multi-character constant. Must be 4 + characters in length. +:returns: True if the key is found, false otherwise +*/ +bool is_key_valid(char *key); + + +/** +Get the current temperature from a sensor + +:param: key The temperature sensor to read from +:param: unit The unit for the temperature value. +:returns: Temperature of sensor. If the sensor is not found, or an error + occurs, return will be zero +*/ +double get_tmp(char *key, tmp_unit_t unit); + + +/** +Is the machine being powered by the battery? + +:returns: True if it is, false otherwise +*/ +bool is_battery_powered(void); + + +/** +Is there a CD in the optical disk drive (ODD)? + +:returns: True if there is, false otherwise +*/ +bool is_optical_disk_drive_full(void); + + +/** +Get the name of a fan. + +:param: fanNum The number of the fan to check +:param: name The name of the fan. Return will be empty on error. +:returns: True if successful, false otherwise. +*/ +bool get_fan_name(unsigned int fan_num, fan_name_t name); + + +/** +Get the number of fans on this machine. + +:returns: The number of fans. If an error occurs, return will be -1. +*/ +int get_num_fans(void); + + +/** +Get the current speed (RPM - revolutions per minute) of a fan. + +:param: fan_num The number of the fan to check +:returns: The fan RPM. If the fan is not found, or an error occurs, return + will be zero +*/ +UInt get_fan_rpm(UInt fan_num); + + +/** +Set the minimum speed (RPM - revolutions per minute) of a fan. This method +requires root privileges. By minimum we mean that OS X can interject and +raise the fan speed if needed, however it will not go below this. + +WARNING: You are playing with hardware here, BE CAREFUL. + +:param: fan_num The number of the fan to set +:param: rpm The speed you would like to set the fan to. +:param: auth Should the function do authentication? +:return: True if successful, false otherwise +*/ +bool set_fan_min_rpm(unsigned int fan_num, unsigned int rpm, bool auth); diff --git a/host/smc.c b/host/smc.c deleted file mode 100644 index 30a232b..0000000 --- a/host/smc.c +++ /dev/null @@ -1,700 +0,0 @@ -/* - * Apple System Management Controller (SMC) API from user space for Intel based - * Macs. Works by talking to the AppleSMC.kext (kernel extension), the driver - * for the SMC. - * - * smc.c - * libsmc - * - * Copyright (C) 2014 beltex - * - * Based off of fork from: - * osx-cpu-temp - * - * With credits to: - * - * Copyright (C) 2006 devnull - * Apple System Management Control (SMC) Tool - * - * Copyright (C) 2006 Hendrik Holtmann - * smcFanControl - * - * This program 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 2 of the License, or - * (at your option) any later version. - * - * This program 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, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include -#include -#include "smc.h" - - -//------------------------------------------------------------------------------ -// MARK: MACROS -//------------------------------------------------------------------------------ - - -/** -Name of the SMC IOService as seen in the IORegistry. You can view it either via -command line with ioreg or through the IORegistryExplorer app (found on Apple's -developer site - Hardware IO Tools for Xcode) -*/ -#define IOSERVICE_SMC "AppleSMC" - - -/** -IOService for getting machine model name -*/ -#define IOSERVICE_MODEL "IOPlatformExpertDevice" - - -/** -SMC data types - 4 byte multi-character constants - -Sources: See TMP SMC keys in smc.h - -http://stackoverflow.com/questions/22160746/fpe2-and-sp78-data-types -*/ -#define DATA_TYPE_UINT8 "ui8 " -#define DATA_TYPE_UINT16 "ui16" -#define DATA_TYPE_UINT32 "ui32" -#define DATA_TYPE_FLAG "flag" -#define DATA_TYPE_FPE2 "fpe2" -#define DATA_TYPE_SFDS "{fds" -#define DATA_TYPE_SP78 "sp78" - - -//------------------------------------------------------------------------------ -// MARK: GLOBAL VARS -//------------------------------------------------------------------------------ - - -/** -Our connection to the SMC -*/ -static io_connect_t conn; - - -/** -Number of characters in an SMC key -*/ -static const int SMC_KEY_SIZE = 4; - - -/** -Number of characters in a data type "key" returned from the SMC. See data type -macros. -*/ -static const int DATA_TYPE_SIZE = 4; - - -//------------------------------------------------------------------------------ -// MARK: ENUMS -//------------------------------------------------------------------------------ - - -/** -Defined by AppleSMC.kext. See SMCParamStruct. - -These are SMC specific return codes -*/ -typedef enum { - kSMCSuccess = 0, - kSMCError = 1, - kSMCKeyNotFound = 0x84 -} kSMC_t; - - -/** -Defined by AppleSMC.kext. See SMCParamStruct. - -Function selectors. Used to tell the SMC which function inside it to call. -*/ -typedef enum { - kSMCUserClientOpen = 0, - kSMCUserClientClose = 1, - kSMCHandleYPCEvent = 2, - kSMCReadKey = 5, - kSMCWriteKey = 6, - kSMCGetKeyCount = 7, - kSMCGetKeyFromIndex = 8, - kSMCGetKeyInfo = 9 -} selector_t; - - -//------------------------------------------------------------------------------ -// MARK: STRUCTS -//------------------------------------------------------------------------------ - - -/** -Defined by AppleSMC.kext. See SMCParamStruct. -*/ -typedef struct { - unsigned char major; - unsigned char minor; - unsigned char build; - unsigned char reserved; - unsigned short release; -} SMCVersion; - - -/** -Defined by AppleSMC.kext. See SMCParamStruct. -*/ -typedef struct { - uint16_t version; - uint16_t length; - uint32_t cpuPLimit; - uint32_t gpuPLimit; - uint32_t memPLimit; -} SMCPLimitData; - - -/** -Defined by AppleSMC.kext. See SMCParamStruct. - -- dataSize : How many values written to SMCParamStruct.bytes -- dataType : Type of data written to SMCParamStruct.bytes. This lets us know how - to interpret it (translate it to human readable) -*/ -typedef struct { - IOByteCount dataSize; - uint32_t dataType; - uint8_t dataAttributes; -} SMCKeyInfoData; - - -/** -Defined by AppleSMC.kext. - -This is the predefined struct that must be passed to communicate with the -AppleSMC driver. While the driver is closed source, the definition of this -struct happened to appear in the Apple PowerManagement project at around -version 211, and soon after disappeared. It can be seen in the PrivateLib.c -file under pmconfigd. - -https://www.opensource.apple.com/source/PowerManagement/PowerManagement-211/ -*/ -typedef struct { - uint32_t key; - SMCVersion vers; - SMCPLimitData pLimitData; - SMCKeyInfoData keyInfo; - uint8_t result; - uint8_t status; - uint8_t data8; - uint32_t data32; - uint8_t bytes[32]; -} SMCParamStruct; - - -/** -Used for returning data from the SMC. -*/ -typedef struct { - uint8_t data[32]; - uint32_t dataType; - uint32_t dataSize; - kSMC_t kSMC; -} smc_return_t; - - -//------------------------------------------------------------------------------ -// MARK: HELPERS - TYPE CONVERSION -//------------------------------------------------------------------------------ - - -/** -Convert data from SMC of fpe2 type to human readable. - -:param: data Data from the SMC to be converted. Assumed data size of 2. -:returns: Converted data -*/ -static unsigned int from_fpe2(uint8_t data[32]) -{ - unsigned int ans = 0; - - // Data type for fan calls - fpe2 - // This is assumend to mean floating point, with 2 exponent bits - // http://stackoverflow.com/questions/22160746/fpe2-and-sp78-data-types - ans += data[0] << 6; - ans += data[1] << 2; - - return ans; -} - - -/** -Convert to fpe2 data type to be passed to SMC. - -:param: val Value to convert -:param: data Pointer to data array to place result -*/ -static void to_fpe2(unsigned int val, uint8_t *data) -{ - data[0] = val >> 6; - data[1] = (val << 2) ^ (data[0] << 8); -} - - -/** -Convert SMC key to uint32_t. This must be done to pass it to the SMC. - -:param: key The SMC key to convert -:returns: uint32_t translation. - Returns zero if key is not 4 characters in length. -*/ -static uint32_t to_uint32_t(char *key) -{ - uint32_t ans = 0; - uint32_t shift = 24; - - // SMC key is expected to be 4 bytes - thus 4 chars - if (strlen(key) != SMC_KEY_SIZE) { - return 0; - } - - for (int i = 0; i < SMC_KEY_SIZE; i++) { - ans += key[i] << shift; - shift -= 8; - } - - return ans; -} - - -/** -For converting the dataType return from the SMC to human readable 4 byte -multi-character constant. -*/ -static void to_string(uint32_t val, char *dataType) -{ - int shift = 24; - - for (int i = 0; i < DATA_TYPE_SIZE; i++) { - // To get each char, we shift it into the lower 8 bits, and then & by - // 255 to insolate it - dataType[i] = (val >> shift) & 0xff; - shift -= 8; - } -} - - -//------------------------------------------------------------------------------ -// MARK: HELPERS - TMP CONVERSION -//------------------------------------------------------------------------------ - - -/** -Celsius to Fahrenheit -*/ -static double to_fahrenheit(double tmp) -{ - // http://en.wikipedia.org/wiki/Fahrenheit#Definition_and_conversions - return (tmp * 1.8) + 32; -} - - -/** -Celsius to Kelvin -*/ -static double to_kelvin(double tmp) -{ - // http://en.wikipedia.org/wiki/Kelvin - return tmp + 273.15; -} - - -//------------------------------------------------------------------------------ -// MARK: "PRIVATE" FUNCTIONS -//------------------------------------------------------------------------------ - - -/** -Make a call to the SMC - -:param: inputStruct Struct that holds data telling the SMC what you want -:param: outputStruct Struct holding the SMC's response -:returns: I/O Kit return code -*/ -static kern_return_t call_smc(SMCParamStruct *inputStruct, - SMCParamStruct *outputStruct) -{ - kern_return_t result; - size_t inputStructCnt = sizeof(SMCParamStruct); - size_t outputStructCnt = sizeof(SMCParamStruct); - - result = IOConnectCallStructMethod(conn, kSMCHandleYPCEvent, - inputStruct, - inputStructCnt, - outputStruct, - &outputStructCnt); - - if (result != kIOReturnSuccess) { - // IOReturn error code lookup. See "Accessing Hardware From Applications - // -> Handling Errors" Apple doc - result = err_get_code(result); - } - - return result; -} - - -/** -Read data from the SMC - -:param: key The SMC key -*/ -static kern_return_t read_smc(char *key, smc_return_t *result_smc) -{ - kern_return_t result; - SMCParamStruct inputStruct; - SMCParamStruct outputStruct; - - memset(&inputStruct, 0, sizeof(SMCParamStruct)); - memset(&outputStruct, 0, sizeof(SMCParamStruct)); - memset(result_smc, 0, sizeof(smc_return_t)); - - // First call to AppleSMC - get key info - inputStruct.key = to_uint32_t(key); - inputStruct.data8 = kSMCGetKeyInfo; - - result = call_smc(&inputStruct, &outputStruct); - result_smc->kSMC = outputStruct.result; - - if (result != kIOReturnSuccess || outputStruct.result != kSMCSuccess) { - return result; - } - - // Store data for return - result_smc->dataSize = outputStruct.keyInfo.dataSize; - result_smc->dataType = outputStruct.keyInfo.dataType; - - - // Second call to AppleSMC - now we can get the data - inputStruct.keyInfo.dataSize = outputStruct.keyInfo.dataSize; - inputStruct.data8 = kSMCReadKey; - - result = call_smc(&inputStruct, &outputStruct); - result_smc->kSMC = outputStruct.result; - - if (result != kIOReturnSuccess || outputStruct.result != kSMCSuccess) { - return result; - } - - memcpy(result_smc->data, outputStruct.bytes, sizeof(outputStruct.bytes)); - - return result; -} - - -/** -Write data to the SMC. - -:returns: IOReturn IOKit return code -*/ -static kern_return_t write_smc(char *key, smc_return_t *result_smc) -{ - kern_return_t result; - SMCParamStruct inputStruct; - SMCParamStruct outputStruct; - - memset(&inputStruct, 0, sizeof(SMCParamStruct)); - memset(&outputStruct, 0, sizeof(SMCParamStruct)); - - // First call to AppleSMC - get key info - inputStruct.key = to_uint32_t(key); - inputStruct.data8 = kSMCGetKeyInfo; - - result = call_smc(&inputStruct, &outputStruct); - result_smc->kSMC = outputStruct.result; - - if (result != kIOReturnSuccess || outputStruct.result != kSMCSuccess) { - return result; - } - - // Check data is correct - if (result_smc->dataSize != outputStruct.keyInfo.dataSize || - result_smc->dataType != outputStruct.keyInfo.dataType) { - return kIOReturnBadArgument; - } - - // Second call to AppleSMC - now we can write the data - inputStruct.data8 = kSMCWriteKey; - inputStruct.keyInfo.dataSize = outputStruct.keyInfo.dataSize; - - // Set data to write - memcpy(inputStruct.bytes, result_smc->data, sizeof(result_smc->data)); - - result = call_smc(&inputStruct, &outputStruct); - result_smc->kSMC = outputStruct.result; - - return result; -} - - -/** -Get the model name of the machine. -*/ -static kern_return_t get_machine_model(io_name_t model) -{ - io_service_t service; - kern_return_t result; - - service = IOServiceGetMatchingService(kIOMasterPortDefault, - IOServiceMatching(IOSERVICE_MODEL)); - - if (service == 0) { - printf("ERROR: %s NOT FOUND\n", IOSERVICE_MODEL); - return kIOReturnError; - } - - // Get the model name - result = IORegistryEntryGetName(service, model); - IOObjectRelease(service); - - return result; -} - - -//------------------------------------------------------------------------------ -// MARK: "PUBLIC" FUNCTIONS -//------------------------------------------------------------------------------ - - -kern_return_t open_smc(void) -{ - kern_return_t result; - io_service_t service; - - service = IOServiceGetMatchingService(kIOMasterPortDefault, - IOServiceMatching(IOSERVICE_SMC)); - - if (service == 0) { - // NOTE: IOServiceMatching documents 0 on failure - printf("ERROR: %s NOT FOUND\n", IOSERVICE_SMC); - return kIOReturnError; - } - - result = IOServiceOpen(service, mach_task_self(), 0, &conn); - IOObjectRelease(service); - - return result; -} - - -kern_return_t close_smc(void) -{ - return IOServiceClose(conn); -} - - -bool is_key_valid(char *key) -{ - bool ans = false; - kern_return_t result; - smc_return_t result_smc; - - if (strlen(key) != SMC_KEY_SIZE) { - printf("ERROR: Invalid key size - must be 4 chars\n"); - return ans; - } - - // Try a read and see if it succeeds - result = read_smc(key, &result_smc); - - if (result == kIOReturnSuccess && result_smc.kSMC == kSMCSuccess) { - ans = true; - } - - return ans; -} - - -double get_tmp(char *key, tmp_unit_t unit) -{ - kern_return_t result; - smc_return_t result_smc; - - result = read_smc(key, &result_smc); - - if (!(result == kIOReturnSuccess && - result_smc.dataSize == 2 && - result_smc.dataType == to_uint32_t(DATA_TYPE_SP78))) { - // Error - return 0.0; - } - - // TODO: Create from_sp78() convert function - double tmp = result_smc.data[0]; - - switch (unit) { - case CELSIUS: - break; - case FAHRENHEIT: - tmp = to_fahrenheit(tmp); - break; - case KELVIN: - tmp = to_kelvin(tmp); - break; - } - - return tmp; -} - - -bool is_battery_powered(void) -{ - kern_return_t result; - smc_return_t result_smc; - - result = read_smc(BATT_PWR, &result_smc); - - if (!(result == kIOReturnSuccess && - result_smc.dataSize == 1 && - result_smc.dataType == to_uint32_t(DATA_TYPE_FLAG))) { - // Error - return false; - } - - return result_smc.data[0]; -} - - -bool is_optical_disk_drive_full(void) -{ - kern_return_t result; - smc_return_t result_smc; - - result = read_smc(ODD_FULL, &result_smc); - - if (!(result == kIOReturnSuccess && - result_smc.dataSize == 1 && - result_smc.dataType == to_uint32_t(DATA_TYPE_FLAG))) { - // Error - return false; - } - - return result_smc.data[0]; -} - - -//------------------------------------------------------------------------------ -// MARK: FAN FUNCTIONS -//------------------------------------------------------------------------------ - - -bool get_fan_name(unsigned int fan_num, fan_name_t name) -{ - char key[5]; - kern_return_t result; - smc_return_t result_smc; - - sprintf(key, "F%dID", fan_num); - result = read_smc(key, &result_smc); - - if (!(result == kIOReturnSuccess && - result_smc.dataSize == 16 && - result_smc.dataType == to_uint32_t(DATA_TYPE_SFDS))) { - return false; - } - - - /* - We know the data size is 16 bytes and the type is "{fds", a custom - struct defined by the AppleSMC.kext. See TMP enum sources for the - struct. - - The last 12 bytes contain the name of the fan, an array of chars, hence - the loop range. - */ - int index = 0; - for (int i = 4; i < 16; i++) { - // Check if at the end (name may not be full 12 bytes) - // Could check for 0 (null), but instead we check for 32 (space). This - // is a hack to remove whitespace. :) - if (result_smc.data[i] == 32) { - break; - } - - name[index] = result_smc.data[i]; - index++; - } - - return true; -} - - -int get_num_fans(void) -{ - kern_return_t result; - smc_return_t result_smc; - - result = read_smc(NUM_FANS, &result_smc); - - if (!(result == kIOReturnSuccess && - result_smc.dataSize == 1 && - result_smc.dataType == to_uint32_t(DATA_TYPE_UINT8))) { - // Error - return -1; - } - - return result_smc.data[0]; -} - - -unsigned int get_fan_rpm(unsigned int fan_num) -{ - char key[5]; - kern_return_t result; - smc_return_t result_smc; - - sprintf(key, "F%dAc", fan_num); - result = read_smc(key, &result_smc); - - if (!(result == kIOReturnSuccess && - result_smc.dataSize == 2 && - result_smc.dataType == to_uint32_t(DATA_TYPE_FPE2))) { - // Error - return 0; - } - - return from_fpe2(result_smc.data); -} - - -bool set_fan_min_rpm(unsigned int fan_num, unsigned int rpm, bool auth) -{ - // TODO: Add rpm val safety check - char key[5]; - bool ans = false; - kern_return_t result; - smc_return_t result_smc; - - memset(&result_smc, 0, sizeof(smc_return_t)); - - // TODO: Don't use magic number - result_smc.dataSize = 2; - result_smc.dataType = to_uint32_t(DATA_TYPE_FPE2); - to_fpe2(rpm, result_smc.data); - - sprintf(key, "F%dMn", fan_num); - result = write_smc(key, &result_smc); - - if (result == kIOReturnSuccess && result_smc.kSMC == kSMCSuccess) { - ans = true; - } - - return ans; -} diff --git a/host/smc.h b/host/smc.h deleted file mode 100644 index b156368..0000000 --- a/host/smc.h +++ /dev/null @@ -1,254 +0,0 @@ -/* - * Apple System Management Controller (SMC) API from user space for Intel based - * Macs. Works by talking to the AppleSMC.kext (kernel extension), the driver - * for the SMC. - * - * smc.h - * libsmc - * - * Copyright (C) 2014 beltex - * - * Based off of fork from: - * osx-cpu-temp - * - * With credits to: - * - * Copyright (C) 2006 devnull - * Apple System Management Control (SMC) Tool - * - * Copyright (C) 2006 Hendrik Holtmann - * smcFanControl - * - * This program 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 2 of the License, or - * (at your option) any later version. - * - * This program 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, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include - - -//------------------------------------------------------------------------------ -// MARK: MACROS -//------------------------------------------------------------------------------ - - -/** -SMC keys for temperature sensors - 4 byte multi-character constants - -Not applicable to all Mac's of course. In adition, the definition of the codes -may not be 100% accurate necessarily. Finally, list is incomplete. - -Presumed letter translations: - -- T = Temperature (if first char) -- C = CPU -- G = GPU -- P = Proximity -- D = Diode -- H = Heatsink - -Sources: - -- https://www.apple.com/downloads/dashboard/status/istatpro.html -- https://github.com/hholtmann/smcFanControl -- https://github.com/jedda/OSX-Monitoring-Tools -- http://www.parhelia.ch/blog/statics/k3_keys.html -*/ -#define AMBIENT_AIR_0 "TA0P" -#define AMBIENT_AIR_1 "TA1P" -#define CPU_0_DIODE "TC0D" -#define CPU_0_HEATSINK "TC0H" -#define CPU_0_PROXIMITY "TC0P" -#define ENCLOSURE_BASE_0 "TB0T" -#define ENCLOSURE_BASE_1 "TB1T" -#define ENCLOSURE_BASE_2 "TB2T" -#define ENCLOSURE_BASE_3 "TB3T" -#define GPU_0_DIODE "TG0D" -#define GPU_0_HEATSINK "TG0H" -#define GPU_0_PROXIMITY "TG0P" -#define HARD_DRIVE_BAY "TH0P" -#define MEMORY_SLOT_0 "TM0S" -#define MEMORY_SLOTS_PROXIMITY "TM0P" -#define NORTHBRIDGE "TN0H" -#define NORTHBRIDGE_DIODE "TN0D" -#define NORTHBRIDGE_PROXIMITY "TN0P" -#define THUNDERBOLT_0 "TI0P" -#define THUNDERBOLT_1 "TI1P" -#define WIRELESS_MODULE "TW0P" - - -/** -SMC keys for fans - 4 byte multi-character constants - -Number of fans on Macs vary of course, thus not all keys will be applicable. - -Presumed letter translations: - -- F = Fan -- Ac = Acutal -- Mn = Min -- Mx = Max -- Sf = Safe -- Tg = Target - -Sources: See TMP SMC keys -*/ -#define FAN_0 "F0Ac" -#define FAN_0_MIN_RPM "F0Mn" -#define FAN_0_MAX_RPM "F0Mx" -#define FAN_0_SAFE_RPM "F0Sf" -#define FAN_0_TARGET_RPM "F0Tg" -#define FAN_1 "F1Ac" -#define FAN_1_MIN_RPM "F1Mn" -#define FAN_1_MAX_RPM "F1Mx" -#define FAN_1_SAFE_RPM "F1Sf" -#define FAN_1_TARGET_RPM "F1Tg" -#define FAN_2 "F2Ac" -#define FAN_2_MIN_RPM "F2Mn" -#define FAN_2_MAX_RPM "F2Mx" -#define FAN_2_SAFE_RPM "F2Sf" -#define FAN_2_TARGET_RPM "F2Tg" -#define NUM_FANS "FNum" -#define FORCE_BITS "FS! " - - -/** -Misc SMC keys - 4 byte multi-character constants - -Sources: See TMP SMC keys -*/ -#define BATT_PWR "BATP" -#define NUM_KEYS "#KEY" -#define ODD_FULL "MSDI" - - -//------------------------------------------------------------------------------ -// MARK: TYPES -//------------------------------------------------------------------------------ - - -typedef char fan_name_t[13]; - - -//------------------------------------------------------------------------------ -// MARK: ENUMS -//------------------------------------------------------------------------------ - - -typedef enum { - CELSIUS, - FAHRENHEIT, - KELVIN -} tmp_unit_t; - - -//------------------------------------------------------------------------------ -// MARK: PROTOTYPES -//------------------------------------------------------------------------------ - - -/** -Open a connection to the SMC - -:returns: kIOReturnSuccess on successful connection to the SMC. -*/ -kern_return_t open_smc(void); - - -/** -Close connection to the SMC - -:returns: kIOReturnSuccess on successful close of connection to the SMC. -*/ -kern_return_t close_smc(void); - - -/** -Check if an SMC key is valid. Useful for determining if a certain machine has -particular sensor or fan for example. - -:param: key The SMC key to check. 4 byte multi-character constant. Must be 4 - characters in length. -:returns: True if the key is found, false otherwise -*/ -bool is_key_valid(char *key); - - -/** -Get the current temperature from a sensor - -:param: key The temperature sensor to read from -:param: unit The unit for the temperature value. -:returns: Temperature of sensor. If the sensor is not found, or an error - occurs, return will be zero -*/ -double get_tmp(char *key, tmp_unit_t unit); - - -/** -Is the machine being powered by the battery? - -:returns: True if it is, false otherwise -*/ -bool is_battery_powered(void); - - -/** -Is there a CD in the optical disk drive (ODD)? - -:returns: True if there is, false otherwise -*/ -bool is_optical_disk_drive_full(void); - - -/** -Get the name of a fan. - -:param: fanNum The number of the fan to check -:param: name The name of the fan. Return will be empty on error. -:returns: True if successful, false otherwise. -*/ -bool get_fan_name(unsigned int fan_num, fan_name_t name); - - -/** -Get the number of fans on this machine. - -:returns: The number of fans. If an error occurs, return will be -1. -*/ -int get_num_fans(void); - - -/** -Get the current speed (RPM - revolutions per minute) of a fan. - -:param: fan_num The number of the fan to check -:returns: The fan RPM. If the fan is not found, or an error occurs, return - will be zero -*/ -UInt get_fan_rpm(UInt fan_num); - - -/** -Set the minimum speed (RPM - revolutions per minute) of a fan. This method -requires root privileges. By minimum we mean that OS X can interject and -raise the fan speed if needed, however it will not go below this. - -WARNING: You are playing with hardware here, BE CAREFUL. - -:param: fan_num The number of the fan to set -:param: rpm The speed you would like to set the fan to. -:param: auth Should the function do authentication? -:return: True if successful, false otherwise -*/ -bool set_fan_min_rpm(unsigned int fan_num, unsigned int rpm, bool auth); From 1c4fa5628c428123d2ceb3463df35ff115442346 Mon Sep 17 00:00:00 2001 From: "CHEVY S. HUNGERFORD" Date: Fri, 26 Jan 2018 20:40:34 -0600 Subject: [PATCH 17/62] giving temperature more verbose output --- host/host_linux.go | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/host/host_linux.go b/host/host_linux.go index e0b4b60..c309ed5 100644 --- a/host/host_linux.go +++ b/host/host_linux.go @@ -591,13 +591,30 @@ func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, err } } - for _, match := range files { - match = strings.Split(match, "_")[0] - name, err := ioutil.ReadFile(filepath.Join(filepath.Dir(match), "name")) + // example directory + // device/ temp1_crit_alarm temp2_crit_alarm temp3_crit_alarm temp4_crit_alarm temp5_crit_alarm temp6_crit_alarm temp7_crit_alarm + // name temp1_input temp2_input temp3_input temp4_input temp5_input temp6_input temp7_input + // power/ temp1_label temp2_label temp3_label temp4_label temp5_label temp6_label temp7_label + // subsystem/ temp1_max temp2_max temp3_max temp4_max temp5_max temp6_max temp7_max + // temp1_crit temp2_crit temp3_crit temp4_crit temp5_crit temp6_crit temp7_crit uevent + for _, file := range files { + if filepath.Match(file, "/class/hwmon/hwmon*/temp*_label") { + continue + } + + filename := strings.Split(filepath.Base(file), "_") + c, _ := ioutil.ReadFile(filepath.Join(filepath.Dir(file), filename[0] + "_label")) + + var corename string + if c != nil { + corename = fmt.Sprintf("%s_", strings.TrimSpace(strings.ToLower(string(c)))) + } + + name, err := ioutil.ReadFile(filepath.Join(filepath.Dir(file), "name")) if err != nil { return temperatures, err } - current, err := ioutil.ReadFile(match + "_input") + current, err := ioutil.ReadFile(file) if err != nil { return temperatures, err } @@ -605,8 +622,10 @@ func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, err if err != nil { continue } + + tempName = strings.TrimSpace(strings.ToLower(string(strings.Join(filename[1:], "")))) temperatures = append(temperatures, TemperatureStat{ - SensorKey: strings.TrimSpace(string(name)), + SensorKey: fmt.Sprintf("%s_%s", strings.TrimSpace(string(name)), corename, tempName), Temperature: temperature / 1000.0, }) } From 29f8dfa1ad243a863c94f394a9bfb922ea7e2da1 Mon Sep 17 00:00:00 2001 From: "CHEVY S. HUNGERFORD" Date: Sat, 27 Jan 2018 10:11:51 -0600 Subject: [PATCH 18/62] fixing SensorKey output --- host/host_linux.go | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/host/host_linux.go b/host/host_linux.go index c309ed5..5324258 100644 --- a/host/host_linux.go +++ b/host/host_linux.go @@ -598,22 +598,27 @@ func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, err // subsystem/ temp1_max temp2_max temp3_max temp4_max temp5_max temp6_max temp7_max // temp1_crit temp2_crit temp3_crit temp4_crit temp5_crit temp6_crit temp7_crit uevent for _, file := range files { - if filepath.Match(file, "/class/hwmon/hwmon*/temp*_label") { + filename := strings.Split(filepath.Base(file), "_") + if filename[1] == "label" { + // Do not try to read the temperature of the label file continue } - filename := strings.Split(filepath.Base(file), "_") - c, _ := ioutil.ReadFile(filepath.Join(filepath.Dir(file), filename[0] + "_label")) - - var corename string + // Get the label of the temperature you are reading + var label string + c, _ := ioutil.ReadFile(filepath.Join(filepath.Dir(file), filename[0]+"_label")) if c != nil { - corename = fmt.Sprintf("%s_", strings.TrimSpace(strings.ToLower(string(c)))) + //format the label from "Core 0" to "core0_" + label = fmt.Sprintf("%s_", strings.Join(strings.Split(strings.TrimSpace(strings.ToLower(string(c))), " "), "")) } + // Get the name of the tempearture you are reading name, err := ioutil.ReadFile(filepath.Join(filepath.Dir(file), "name")) if err != nil { return temperatures, err } + + // Get the temperature reading current, err := ioutil.ReadFile(file) if err != nil { return temperatures, err @@ -623,9 +628,9 @@ func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, err continue } - tempName = strings.TrimSpace(strings.ToLower(string(strings.Join(filename[1:], "")))) + tempName := strings.TrimSpace(strings.ToLower(string(strings.Join(filename[1:], "")))) temperatures = append(temperatures, TemperatureStat{ - SensorKey: fmt.Sprintf("%s_%s", strings.TrimSpace(string(name)), corename, tempName), + SensorKey: fmt.Sprintf("%s_%s%s", strings.TrimSpace(string(name)), label, tempName), Temperature: temperature / 1000.0, }) } From 178e5ce997a611592ed881b61258d6dd6e03e555 Mon Sep 17 00:00:00 2001 From: Jaime Fullaondo Date: Mon, 12 Feb 2018 15:04:20 +0100 Subject: [PATCH 19/62] [host] fix race condition to ret --- cpu/cpu_windows.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/cpu/cpu_windows.go b/cpu/cpu_windows.go index af65721..a95df9f 100644 --- a/cpu/cpu_windows.go +++ b/cpu/cpu_windows.go @@ -132,6 +132,10 @@ func PerfInfoWithContext(ctx context.Context) ([]Win32_PerfFormattedData_Counter ctx, cancel := context.WithTimeout(context.Background(), common.Timeout) defer cancel() err := common.WMIQueryWithContext(ctx, q, &ret) + if err != nil { + return Win32_PerfFormattedData_Counters_ProcessorInformation{}, err + } + return ret, err } @@ -147,6 +151,9 @@ func ProcInfoWithContext(ctx context.Context) ([]Win32_PerfFormattedData_PerfOS_ ctx, cancel := context.WithTimeout(context.Background(), common.Timeout) defer cancel() err := common.WMIQueryWithContext(ctx, q, &ret) + if err != nil { + return Win32_PerfFormattedData_PerfOS_System{}, err + } return ret, err } From 8dd42707e2221e26bdb87c095aeaab4e7d3a8487 Mon Sep 17 00:00:00 2001 From: sada Date: Mon, 12 Feb 2018 13:24:40 -0800 Subject: [PATCH 20/62] windows: fix WithContext functions set timeout only if parent context doesn't set. --- cpu/cpu_windows.go | 6 ------ disk/disk_windows.go | 2 -- host/host_windows.go | 8 +++----- internal/common/common_windows.go | 6 ++++++ process/process_windows.go | 4 ---- 5 files changed, 9 insertions(+), 17 deletions(-) diff --git a/cpu/cpu_windows.go b/cpu/cpu_windows.go index af65721..fd7905c 100644 --- a/cpu/cpu_windows.go +++ b/cpu/cpu_windows.go @@ -90,8 +90,6 @@ func InfoWithContext(ctx context.Context) ([]InfoStat, error) { var ret []InfoStat var dst []Win32_Processor q := wmi.CreateQuery(&dst, "") - ctx, cancel := context.WithTimeout(context.Background(), common.Timeout) - defer cancel() if err := common.WMIQueryWithContext(ctx, q, &dst); err != nil { return ret, err } @@ -129,8 +127,6 @@ func PerfInfoWithContext(ctx context.Context) ([]Win32_PerfFormattedData_Counter var ret []Win32_PerfFormattedData_Counters_ProcessorInformation q := wmi.CreateQuery(&ret, "") - ctx, cancel := context.WithTimeout(context.Background(), common.Timeout) - defer cancel() err := common.WMIQueryWithContext(ctx, q, &ret) return ret, err } @@ -144,8 +140,6 @@ func ProcInfo() ([]Win32_PerfFormattedData_PerfOS_System, error) { func ProcInfoWithContext(ctx context.Context) ([]Win32_PerfFormattedData_PerfOS_System, error) { var ret []Win32_PerfFormattedData_PerfOS_System q := wmi.CreateQuery(&ret, "") - ctx, cancel := context.WithTimeout(context.Background(), common.Timeout) - defer cancel() err := common.WMIQueryWithContext(ctx, q, &ret) return ret, err } diff --git a/disk/disk_windows.go b/disk/disk_windows.go index 7389b5a..326bc1f 100644 --- a/disk/disk_windows.go +++ b/disk/disk_windows.go @@ -144,8 +144,6 @@ func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOC ret := make(map[string]IOCountersStat, 0) var dst []Win32_PerfFormattedData - ctx, cancel := context.WithTimeout(context.Background(), common.Timeout) - defer cancel() err := common.WMIQueryWithContext(ctx, "SELECT * FROM Win32_PerfFormattedData_PerfDisk_LogicalDisk", &dst) if err != nil { return ret, err diff --git a/host/host_windows.go b/host/host_windows.go index 643bd66..7897319 100644 --- a/host/host_windows.go +++ b/host/host_windows.go @@ -48,7 +48,7 @@ func InfoWithContext(ctx context.Context) (*InfoStat, error) { } { - platform, family, version, err := PlatformInformation() + platform, family, version, err := PlatformInformationWithContext(ctx) if err == nil { ret.Platform = platform ret.PlatformFamily = family @@ -118,8 +118,6 @@ func GetOSInfo() (Win32_OperatingSystem, error) { func GetOSInfoWithContext(ctx context.Context) (Win32_OperatingSystem, error) { var dst []Win32_OperatingSystem q := wmi.CreateQuery(&dst, "") - ctx, cancel := context.WithTimeout(context.Background(), common.Timeout) - defer cancel() err := common.WMIQueryWithContext(ctx, q, &dst) if err != nil { return Win32_OperatingSystem{}, err @@ -136,7 +134,7 @@ func Uptime() (uint64, error) { func UptimeWithContext(ctx context.Context) (uint64, error) { if osInfo == nil { - _, err := GetOSInfo() + _, err := GetOSInfoWithContext(ctx) if err != nil { return 0, err } @@ -177,7 +175,7 @@ func PlatformInformation() (platform string, family string, version string, err func PlatformInformationWithContext(ctx context.Context) (platform string, family string, version string, err error) { if osInfo == nil { - _, err = GetOSInfo() + _, err = GetOSInfoWithContext(ctx) if err != nil { return } diff --git a/internal/common/common_windows.go b/internal/common/common_windows.go index 1dffe61..b02c5cf 100644 --- a/internal/common/common_windows.go +++ b/internal/common/common_windows.go @@ -115,6 +115,12 @@ func CreateCounter(query windows.Handle, pname, cname string) (*CounterInfo, err // WMIQueryWithContext - wraps wmi.Query with a timed-out context to avoid hanging func WMIQueryWithContext(ctx context.Context, query string, dst interface{}, connectServerArgs ...interface{}) error { + if _, ok := ctx.Deadline(); !ok { + ctxTimeout, cancel := context.WithTimeout(ctx, Timeout) + defer cancel() + ctx = ctxTimeout + } + errChan := make(chan error, 1) go func() { errChan <- wmi.Query(query, dst, connectServerArgs...) diff --git a/process/process_windows.go b/process/process_windows.go index d35fcc2..f4c8fe2 100644 --- a/process/process_windows.go +++ b/process/process_windows.go @@ -144,8 +144,6 @@ func GetWin32ProcWithContext(ctx context.Context, pid int32) ([]Win32_Process, e var dst []Win32_Process query := fmt.Sprintf("WHERE ProcessId = %d", pid) q := wmi.CreateQuery(&dst, query) - ctx, cancel := context.WithTimeout(context.Background(), common.Timeout) - defer cancel() err := common.WMIQueryWithContext(ctx, q, &dst) if err != nil { return []Win32_Process{}, fmt.Errorf("could not get win32Proc: %s", err) @@ -457,8 +455,6 @@ func (p *Process) Children() ([]*Process, error) { func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) { var dst []Win32_Process query := wmi.CreateQuery(&dst, fmt.Sprintf("Where ParentProcessId = %d", p.Pid)) - ctx, cancel := context.WithTimeout(context.Background(), common.Timeout) - defer cancel() err := common.WMIQueryWithContext(ctx, query, &dst) if err != nil { return nil, err From 9a74b6fe2e974ba2aa8a991608de7350210500c5 Mon Sep 17 00:00:00 2001 From: shirou Date: Tue, 13 Feb 2018 22:21:27 +0900 Subject: [PATCH 21/62] [cpu]windows: fix retrun value. --- cpu/cpu_windows.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpu/cpu_windows.go b/cpu/cpu_windows.go index a95df9f..a44a089 100644 --- a/cpu/cpu_windows.go +++ b/cpu/cpu_windows.go @@ -133,7 +133,7 @@ func PerfInfoWithContext(ctx context.Context) ([]Win32_PerfFormattedData_Counter defer cancel() err := common.WMIQueryWithContext(ctx, q, &ret) if err != nil { - return Win32_PerfFormattedData_Counters_ProcessorInformation{}, err + return []Win32_PerfFormattedData_Counters_ProcessorInformation{}, err } return ret, err @@ -152,7 +152,7 @@ func ProcInfoWithContext(ctx context.Context) ([]Win32_PerfFormattedData_PerfOS_ defer cancel() err := common.WMIQueryWithContext(ctx, q, &ret) if err != nil { - return Win32_PerfFormattedData_PerfOS_System{}, err + return []Win32_PerfFormattedData_PerfOS_System{}, err } return ret, err } From 543a05cce094293c7747322720256bee15d88a12 Mon Sep 17 00:00:00 2001 From: shirou Date: Tue, 20 Feb 2018 16:00:26 +0900 Subject: [PATCH 22/62] fix psutil URL. --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 9a707ba..bc4614f 100644 --- a/README.rst +++ b/README.rst @@ -10,7 +10,7 @@ gopsutil: psutil for golang .. image:: https://godoc.org/github.com/shirou/gopsutil?status.svg :target: http://godoc.org/github.com/shirou/gopsutil -This is a port of psutil (http://pythonhosted.org/psutil/). The challenge is porting all +This is a port of psutil (https://github.com/giampaolo/psutil). The challenge is porting all psutil functions on some architectures. @@ -296,7 +296,7 @@ Related Works I have been influenced by the following great works: -- psutil: http://pythonhosted.org/psutil/ +- psutil: https://github.com/giampaolo/psutil - dstat: https://github.com/dagwieers/dstat - gosigar: https://github.com/cloudfoundry/gosigar/ - goprocinfo: https://github.com/c9s/goprocinfo From 44ef466a5ac4e209e0b984df49574ef5db038746 Mon Sep 17 00:00:00 2001 From: Ihde Date: Tue, 20 Feb 2018 13:12:09 -0500 Subject: [PATCH 23/62] Resolve issue #492 to reduce CPU overhead associated with fillFromStat --- process/process_linux.go | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/process/process_linux.go b/process/process_linux.go index 7a5ebb0..8f59949 100644 --- a/process/process_linux.go +++ b/process/process_linux.go @@ -235,10 +235,15 @@ func (p *Process) Terminal() (string, error) { } func (p *Process) TerminalWithContext(ctx context.Context) (string, error) { - terminal, _, _, _, _, _, err := p.fillFromStat() + t, _, _, _, _, _, err := p.fillFromStat() if err != nil { return "", err } + termmap, err := getTerminalMap() + if err != nil { + return "", err + } + terminal := termmap[t] return terminal, nil } @@ -1115,11 +1120,11 @@ func (p *Process) fillFromStatusWithContext(ctx context.Context) error { return nil } -func (p *Process) fillFromTIDStat(tid int32) (string, int32, *cpu.TimesStat, int64, uint32, int32, error) { +func (p *Process) fillFromTIDStat(tid int32) (uint64, int32, *cpu.TimesStat, int64, uint32, int32, error) { return p.fillFromTIDStatWithContext(context.Background(), tid) } -func (p *Process) fillFromTIDStatWithContext(ctx context.Context, tid int32) (string, int32, *cpu.TimesStat, int64, uint32, int32, error) { +func (p *Process) fillFromTIDStatWithContext(ctx context.Context, tid int32) (uint64, int32, *cpu.TimesStat, int64, uint32, int32, error) { pid := p.Pid var statPath string @@ -1131,7 +1136,7 @@ func (p *Process) fillFromTIDStatWithContext(ctx context.Context, tid int32) (st contents, err := ioutil.ReadFile(statPath) if err != nil { - return "", 0, nil, 0, 0, 0, err + return 0, 0, nil, 0, 0, 0, err } fields := strings.Fields(string(contents)) @@ -1140,28 +1145,23 @@ func (p *Process) fillFromTIDStatWithContext(ctx context.Context, tid int32) (st i++ } - termmap, err := getTerminalMap() - terminal := "" - if err == nil { - t, err := strconv.ParseUint(fields[i+5], 10, 64) - if err != nil { - return "", 0, nil, 0, 0, 0, err - } - terminal = termmap[t] + terminal, err := strconv.ParseUint(fields[i+5], 10, 64) + if err != nil { + return 0, 0, nil, 0, 0, 0, err } ppid, err := strconv.ParseInt(fields[i+2], 10, 32) if err != nil { - return "", 0, nil, 0, 0, 0, err + return 0, 0, nil, 0, 0, 0, err } utime, err := strconv.ParseFloat(fields[i+12], 64) if err != nil { - return "", 0, nil, 0, 0, 0, err + return 0, 0, nil, 0, 0, 0, err } stime, err := strconv.ParseFloat(fields[i+13], 64) if err != nil { - return "", 0, nil, 0, 0, 0, err + return 0, 0, nil, 0, 0, 0, err } cpuTimes := &cpu.TimesStat{ @@ -1173,7 +1173,7 @@ func (p *Process) fillFromTIDStatWithContext(ctx context.Context, tid int32) (st bootTime, _ := host.BootTime() t, err := strconv.ParseUint(fields[i+20], 10, 64) if err != nil { - return "", 0, nil, 0, 0, 0, err + return 0, 0, nil, 0, 0, 0, err } ctime := (t / uint64(ClockTicks)) + uint64(bootTime) createTime := int64(ctime * 1000) @@ -1193,11 +1193,11 @@ func (p *Process) fillFromTIDStatWithContext(ctx context.Context, tid int32) (st return terminal, int32(ppid), cpuTimes, createTime, uint32(rtpriority), nice, nil } -func (p *Process) fillFromStat() (string, int32, *cpu.TimesStat, int64, uint32, int32, error) { +func (p *Process) fillFromStat() (uint64, int32, *cpu.TimesStat, int64, uint32, int32, error) { return p.fillFromStatWithContext(context.Background()) } -func (p *Process) fillFromStatWithContext(ctx context.Context) (string, int32, *cpu.TimesStat, int64, uint32, int32, error) { +func (p *Process) fillFromStatWithContext(ctx context.Context) (uint64, int32, *cpu.TimesStat, int64, uint32, int32, error) { return p.fillFromTIDStat(-1) } From b11d0a7942c079d28f4ac8765fd7986e73a1c0da Mon Sep 17 00:00:00 2001 From: Michael Schurter Date: Tue, 27 Feb 2018 11:27:49 -0800 Subject: [PATCH 24/62] typo: wich -> which --- process/process_windows.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/process/process_windows.go b/process/process_windows.go index f4c8fe2..9304e2d 100644 --- a/process/process_windows.go +++ b/process/process_windows.go @@ -400,7 +400,7 @@ func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) } // User and kernel times are represented as a FILETIME structure - // wich contains a 64-bit value representing the number of + // which contains a 64-bit value representing the number of // 100-nanosecond intervals since January 1, 1601 (UTC): // http://msdn.microsoft.com/en-us/library/ms724284(VS.85).aspx // To convert it into a float representing the seconds that the From 28f7cb0d630a57522131b9a2d005b666898ae8e9 Mon Sep 17 00:00:00 2001 From: shirou Date: Sun, 11 Mar 2018 13:22:13 +0900 Subject: [PATCH 25/62] [host] linux: use uptime even if docker guest. --- host/host_linux.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/host/host_linux.go b/host/host_linux.go index 5324258..8f6e02f 100644 --- a/host/host_linux.go +++ b/host/host_linux.go @@ -113,6 +113,9 @@ func BootTimeWithContext(ctx context.Context) (uint64, error) { if system == "lxc" && role == "guest" { // if lxc, /proc/uptime is used. statFile = "uptime" + } else if system == "docker" && role == "guest" { + // also docker, guest + statFile = "uptime" } filename := common.HostProc(statFile) From d968f63e51fb9ea514a933e21d571a40fe358637 Mon Sep 17 00:00:00 2001 From: Steven Hartland Date: Sun, 11 Mar 2018 22:10:31 +0000 Subject: [PATCH 26/62] Eliminate call to swapinfo on FreeBSD Improve performance by eliminating the fork out to swapinfo on FreeBSD which also helps prevent crashes / hangs due to the outstanding fork crash bug: golang/go#15658 This also fixes the value reported by SwapMemory and SwapMemoryWithContext on FreeBSD which previously only included the first swap device and also reported the values in terms of 1K blocks instead of bytes. --- mem/mem_freebsd.go | 75 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 32 deletions(-) diff --git a/mem/mem_freebsd.go b/mem/mem_freebsd.go index e691227..1fa880b 100644 --- a/mem/mem_freebsd.go +++ b/mem/mem_freebsd.go @@ -5,9 +5,7 @@ package mem import ( "context" "errors" - "os/exec" - "strconv" - "strings" + "unsafe" "golang.org/x/sys/unix" ) @@ -69,53 +67,66 @@ func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) { } // Return swapinfo -// FreeBSD can have multiple swap devices. but use only first device func SwapMemory() (*SwapMemoryStat, error) { return SwapMemoryWithContext(context.Background()) } +// Constants from vm/vm_param.h +// nolint: golint +const ( + XSWDEV_VERSION = 1 +) + +// Types from vm/vm_param.h +type xswdev struct { + Version uint32 // Version is the version + Dev uint32 // Dev is the device identifier + Flags int32 // Flags is the swap flags applied to the device + NBlks int32 // NBlks is the total number of blocks + Used int32 // Used is the number of blocks used +} + func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) { - swapinfo, err := exec.LookPath("swapinfo") + // FreeBSD can have multiple swap devices so we total them up + i, err := unix.SysctlUint32("vm.nswapdev") if err != nil { return nil, err } - out, err := invoke.Command(swapinfo) + if i == 0 { + return nil, errors.New("no swap devices found") + } + + c := int(i) + + i, err = unix.SysctlUint32("vm.stats.vm.v_page_size") if err != nil { return nil, err } - for _, line := range strings.Split(string(out), "\n") { - values := strings.Fields(line) - // skip title line - if len(values) == 0 || values[0] == "Device" { - continue - } + pageSize := uint64(i) - u := strings.Replace(values[4], "%", "", 1) - total_v, err := strconv.ParseUint(values[1], 10, 64) - if err != nil { - return nil, err - } - used_v, err := strconv.ParseUint(values[2], 10, 64) - if err != nil { - return nil, err - } - free_v, err := strconv.ParseUint(values[3], 10, 64) + var buf []byte + s := &SwapMemoryStat{} + for n := 0; n < c; n++ { + buf, err = unix.SysctlRaw("vm.swap_info", n) if err != nil { return nil, err } - up_v, err := strconv.ParseFloat(u, 64) - if err != nil { - return nil, err + + xsw := (*xswdev)(unsafe.Pointer(&buf[0])) + if xsw.Version != XSWDEV_VERSION { + return nil, errors.New("xswdev version mismatch") } + s.Total += uint64(xsw.NBlks) + s.Used += uint64(xsw.Used) + } - return &SwapMemoryStat{ - Total: total_v, - Used: used_v, - Free: free_v, - UsedPercent: up_v, - }, nil + if s.Total != 0 { + s.UsedPercent = float64(s.Used) / float64(s.Total) * 100 } + s.Total *= pageSize + s.Used *= pageSize + s.Free = s.Total - s.Used - return nil, errors.New("no swap devices found") + return s, nil } From f846eda92320c2e39d831c90d68b3ad06d5ebcb0 Mon Sep 17 00:00:00 2001 From: Steven Hartland Date: Sun, 11 Mar 2018 23:08:04 +0000 Subject: [PATCH 27/62] Eliminate call to uname on FreeBSD Improve performance by eliminating the fork out to uname on FreeBSD which also helps prevent crashes / hangs due to the outstanding fork crash bug: golang/go#15658 Also added a test for PlatformInformation. --- host/host_freebsd.go | 19 +++++-------------- host/host_test.go | 12 ++++++++++++ 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/host/host_freebsd.go b/host/host_freebsd.go index 2f51c31..00a8519 100644 --- a/host/host_freebsd.go +++ b/host/host_freebsd.go @@ -8,7 +8,6 @@ import ( "encoding/binary" "io/ioutil" "os" - "os/exec" "runtime" "strings" "sync/atomic" @@ -168,25 +167,17 @@ func PlatformInformation() (string, string, string, error) { } func PlatformInformationWithContext(ctx context.Context) (string, string, string, error) { - platform := "" - family := "" - version := "" - uname, err := exec.LookPath("uname") + platform, err := unix.Sysctl("kern.ostype") if err != nil { return "", "", "", err } - out, err := invoke.Command(uname, "-s") - if err == nil { - platform = strings.ToLower(strings.TrimSpace(string(out))) - } - - out, err = invoke.Command(uname, "-r") - if err == nil { - version = strings.ToLower(strings.TrimSpace(string(out))) + version, err := unix.Sysctl("kern.osrelease") + if err != nil { + return "", "", "", err } - return platform, family, version, nil + return strings.ToLower(platform), "", strings.ToLower(version), nil } func Virtualization() (string, string, error) { diff --git a/host/host_test.go b/host/host_test.go index b4f3e40..e6abb78 100644 --- a/host/host_test.go +++ b/host/host_test.go @@ -148,3 +148,15 @@ func TestKernelVersion(t *testing.T) { t.Logf("KernelVersion(): %s", version) } + +func TestPlatformInformation(t *testing.T) { + platform, family, version, err := PlatformInformation() + if err != nil { + t.Errorf("PlatformInformation() failed, %v", err) + } + if platform == "" { + t.Errorf("PlatformInformation() retuns empty: %v", platform) + } + + t.Logf("PlatformInformation(): %v, %v, %v", platform, family, version) +} From ee5d6a3d8f31ca3c97f38bcb712772e656fcc379 Mon Sep 17 00:00:00 2001 From: linuxhe Date: Tue, 13 Mar 2018 14:46:16 +0800 Subject: [PATCH 28/62] fix docker uptime --- host/host_linux.go | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/host/host_linux.go b/host/host_linux.go index 8f6e02f..098d086 100644 --- a/host/host_linux.go +++ b/host/host_linux.go @@ -109,6 +109,7 @@ func BootTimeWithContext(ctx context.Context) (uint64, error) { if err != nil { return 0, err } + statFile := "stat" if system == "lxc" && role == "guest" { // if lxc, /proc/uptime is used. @@ -123,20 +124,35 @@ func BootTimeWithContext(ctx context.Context) (uint64, error) { if err != nil { return 0, err } - for _, line := range lines { - if strings.HasPrefix(line, "btime") { - f := strings.Fields(line) - if len(f) != 2 { - return 0, fmt.Errorf("wrong btime format") - } - b, err := strconv.ParseInt(f[1], 10, 64) - if err != nil { - return 0, err + + if statFile == "stat" { + for _, line := range lines { + if strings.HasPrefix(line, "btime") { + f := strings.Fields(line) + if len(f) != 2 { + return 0, fmt.Errorf("wrong btime format") + } + b, err := strconv.ParseInt(f[1], 10, 64) + if err != nil { + return 0, err + } + t = uint64(b) + atomic.StoreUint64(&cachedBootTime, t) + return t, nil } - t = uint64(b) - atomic.StoreUint64(&cachedBootTime, t) - return t, nil } + } else if statFile == "uptime" { + if len(lines) != 1 { + return 0, fmt.Errorf("wrong uptime format") + } + f := strings.Fields(lines[0]) + b, err := strconv.ParseInt(f[0], 10, 64) + if err != nil { + return 0, err + } + t = uint64(time.Now().Unix()) - uint64(b) + atomic.StoreUint64(&cachedBootTime, t) + return t, nil } return 0, fmt.Errorf("could not find btime") From 413cb32b6c0b37a5721e08ed87ac7f0cb425917c Mon Sep 17 00:00:00 2001 From: Laurie Clark-Michalek Date: Wed, 21 Mar 2018 14:08:39 +0000 Subject: [PATCH 29/62] Use static initialisation for invoke instances, instead of init funcs The order of init function execution is dependant on the order that the source files are passed to the compiler. This causes issues when building under other build systems, such as bazel or buck, as they are not guarenteed to maintain the same file order as the default go tool. --- cpu/cpu.go | 3 +-- disk/disk.go | 6 +----- docker/docker.go | 6 +----- host/host.go | 6 +----- load/load.go | 6 +----- mem/mem.go | 6 +----- net/net.go | 6 +----- process/process.go | 6 +----- 8 files changed, 8 insertions(+), 37 deletions(-) diff --git a/cpu/cpu.go b/cpu/cpu.go index 049869e..ceaf77f 100644 --- a/cpu/cpu.go +++ b/cpu/cpu.go @@ -54,10 +54,9 @@ type lastPercent struct { } var lastCPUPercent lastPercent -var invoke common.Invoker +var invoke common.Invoker = common.Invoke{} func init() { - invoke = common.Invoke{} lastCPUPercent.Lock() lastCPUPercent.lastCPUTimes, _ = Times(false) lastCPUPercent.lastPerCPUTimes, _ = Times(true) diff --git a/disk/disk.go b/disk/disk.go index a2c4720..1c31047 100644 --- a/disk/disk.go +++ b/disk/disk.go @@ -6,11 +6,7 @@ import ( "github.com/shirou/gopsutil/internal/common" ) -var invoke common.Invoker - -func init() { - invoke = common.Invoke{} -} +var invoke common.Invoker = common.Invoke{} type UsageStat struct { Path string `json:"path"` diff --git a/docker/docker.go b/docker/docker.go index 83716fd..76791c2 100644 --- a/docker/docker.go +++ b/docker/docker.go @@ -10,11 +10,7 @@ import ( var ErrDockerNotAvailable = errors.New("docker not available") var ErrCgroupNotAvailable = errors.New("cgroup not available") -var invoke common.Invoker - -func init() { - invoke = common.Invoke{} -} +var invoke common.Invoker = common.Invoke{} type CgroupMemStat struct { ContainerID string `json:"containerID"` diff --git a/host/host.go b/host/host.go index a256a6c..1e9e9bb 100644 --- a/host/host.go +++ b/host/host.go @@ -6,11 +6,7 @@ import ( "github.com/shirou/gopsutil/internal/common" ) -var invoke common.Invoker - -func init() { - invoke = common.Invoke{} -} +var invoke common.Invoker = common.Invoke{} // A HostInfoStat describes the host status. // This is not in the psutil but it useful. diff --git a/load/load.go b/load/load.go index dfe32a1..9085889 100644 --- a/load/load.go +++ b/load/load.go @@ -6,11 +6,7 @@ import ( "github.com/shirou/gopsutil/internal/common" ) -var invoke common.Invoker - -func init() { - invoke = common.Invoke{} -} +var invoke common.Invoker = common.Invoke{} type AvgStat struct { Load1 float64 `json:"load1"` diff --git a/mem/mem.go b/mem/mem.go index 87dfb53..bdf15ba 100644 --- a/mem/mem.go +++ b/mem/mem.go @@ -6,11 +6,7 @@ import ( "github.com/shirou/gopsutil/internal/common" ) -var invoke common.Invoker - -func init() { - invoke = common.Invoke{} -} +var invoke common.Invoker = common.Invoke{} // Memory usage statistics. Total, Available and Used contain numbers of bytes // for human consumption. diff --git a/net/net.go b/net/net.go index 428e68e..c31f512 100644 --- a/net/net.go +++ b/net/net.go @@ -12,11 +12,7 @@ import ( "github.com/shirou/gopsutil/internal/common" ) -var invoke common.Invoker - -func init() { - invoke = common.Invoke{} -} +var invoke common.Invoker = common.Invoke{} type IOCountersStat struct { Name string `json:"name"` // interface name diff --git a/process/process.go b/process/process.go index e20742a..29d114e 100644 --- a/process/process.go +++ b/process/process.go @@ -11,11 +11,7 @@ import ( "github.com/shirou/gopsutil/mem" ) -var invoke common.Invoker - -func init() { - invoke = common.Invoke{} -} +var invoke common.Invoker = common.Invoke{} type Process struct { Pid int32 `json:"pid"` From ebe614f6e1b8177450bbc2baeb862220a8b573cf Mon Sep 17 00:00:00 2001 From: shirou Date: Thu, 22 Mar 2018 00:28:00 +0900 Subject: [PATCH 30/62] [solaris] host: add PlatformInformation() which is missing. --- host/host_solaris.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/host/host_solaris.go b/host/host_solaris.go index 2560c06..d5657db 100644 --- a/host/host_solaris.go +++ b/host/host_solaris.go @@ -231,3 +231,18 @@ func KernelVersionWithContext(ctx context.Context) (string, error) { } return "", fmt.Errorf("could not get kernel version") } + +func PlatformInformation() (platform string, family string, version string, err error) { + return PlatformInformationWithContext(context.Background()) +} + +func PlatformInformationWithContext(ctx context.Context) (platform string, family string, version string, err error) { + /* This is not finished yet at all. Please contribute! */ + + version, err = KernelVersion() + if err != nil { + return "", "", "", err + } + + return "solaris", "solaris", version, nil +} From 26a4a2f995ce72e0f735d539fb767291a71f04af Mon Sep 17 00:00:00 2001 From: shirou Date: Sat, 24 Mar 2018 15:56:32 +0900 Subject: [PATCH 31/62] [process]: move ErrorNoChildren to platform independent. --- process/process.go | 6 +++++- process/process_linux.go | 6 +----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/process/process.go b/process/process.go index 29d114e..a4b24f4 100644 --- a/process/process.go +++ b/process/process.go @@ -3,6 +3,7 @@ package process import ( "context" "encoding/json" + "errors" "runtime" "time" @@ -11,7 +12,10 @@ import ( "github.com/shirou/gopsutil/mem" ) -var invoke common.Invoker = common.Invoke{} +var ( + invoke common.Invoker = common.Invoke{} + ErrorNoChildren = errors.New("process does not have children") +) type Process struct { Pid int32 `json:"pid"` diff --git a/process/process_linux.go b/process/process_linux.go index 8f59949..894f268 100644 --- a/process/process_linux.go +++ b/process/process_linux.go @@ -7,7 +7,6 @@ import ( "bytes" "context" "encoding/json" - "errors" "fmt" "io/ioutil" "math" @@ -23,10 +22,7 @@ import ( "golang.org/x/sys/unix" ) -var ( - ErrorNoChildren = errors.New("process does not have children") - PageSize = uint64(os.Getpagesize()) -) +var PageSize = uint64(os.Getpagesize()) const ( PrioProcess = 0 // linux/resource.h From b99342a9ceb013854c12cfb0b55c2adc57837a29 Mon Sep 17 00:00:00 2001 From: shirou Date: Sat, 24 Mar 2018 15:57:29 +0900 Subject: [PATCH 32/62] [host] fallback: implement PlatformInformation() --- host/host_fallback.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/host/host_fallback.go b/host/host_fallback.go index f6537a9..e80d7ea 100644 --- a/host/host_fallback.go +++ b/host/host_fallback.go @@ -55,3 +55,11 @@ func KernelVersion() (string, error) { func KernelVersionWithContext(ctx context.Context) (string, error) { return "", common.ErrNotImplementedError } + +func PlatformInformation() (string, string, string, error) { + return PlatformInformationWithContext(context.Background()) +} + +func PlatformInformationWithContext(ctx context.Context) (string, string, string, error) { + return "", "", "", common.ErrNotImplementedError +} From 699deed6c62a0859e1ef9be33d63bcc0f3f0c37a Mon Sep 17 00:00:00 2001 From: Chevy Date: Mon, 26 Mar 2018 12:16:33 -0500 Subject: [PATCH 33/62] Fix float64 casting --- mem/mem_windows.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mem/mem_windows.go b/mem/mem_windows.go index d40f6cf..cc9ce82 100644 --- a/mem/mem_windows.go +++ b/mem/mem_windows.go @@ -84,7 +84,7 @@ func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) { Total: tot, Used: used, Free: free, - UsedPercent: float64(used / tot), + UsedPercent: float64(used) / float64(tot), } return ret, nil From 4510db20dbab6fdd64061e5d6c7e93015c115fd5 Mon Sep 17 00:00:00 2001 From: Dimitris Rozakis Date: Mon, 26 Mar 2018 21:38:02 +0300 Subject: [PATCH 34/62] Always calculate linux used mem as total-(free+buffered+cache) Used memory was calculated as Total - Available. For newer kernels (3.14+), available memory is taken from /proc/meminfo, whereas for older kernels it is computed as free+buffered+cached. This commit changes that behavior. Available memory is still taken from /proc/meminfo, if available, but used memory is always computed as total-free-buffered-cached. This way, it matches the output of `free` for used memory (in the -/+ buffers/cache line) and other tools. Prior to this change, I'd see a reported used memory of 600MiB whereas free, htop and other tools would report a used memory of 1.8GiB. And adding used, cached, buffered and free memory would leave ~1.2GiB unaccounted for. --- mem/mem_linux.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mem/mem_linux.go b/mem/mem_linux.go index fe6c4e1..33b2490 100644 --- a/mem/mem_linux.go +++ b/mem/mem_linux.go @@ -70,8 +70,8 @@ func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) { if !memavail { ret.Available = ret.Free + ret.Buffers + ret.Cached } - ret.Used = ret.Total - ret.Available - ret.UsedPercent = float64(ret.Total-ret.Available) / float64(ret.Total) * 100.0 + ret.Used = ret.Total - ret.Free - ret.Buffers - ret.Cached + ret.UsedPercent = float64(ret.Used) / float64(ret.Total) * 100.0 return ret, nil } From 1ba5059aca5547f6ecee7024d42bfbfb4d7accac Mon Sep 17 00:00:00 2001 From: shirou Date: Wed, 28 Mar 2018 10:57:05 +0900 Subject: [PATCH 35/62] [memory] Linux: add document url about OverCommiting. --- mem/mem.go | 1 + 1 file changed, 1 insertion(+) diff --git a/mem/mem.go b/mem/mem.go index 6eb8cf7..fd8337a 100644 --- a/mem/mem.go +++ b/mem/mem.go @@ -45,6 +45,7 @@ type VirtualMemoryStat struct { // Linux specific numbers // https://www.centos.org/docs/5/html/5.1/Deployment_Guide/s2-proc-meminfo.html // https://www.kernel.org/doc/Documentation/filesystems/proc.txt + // https://www.kernel.org/doc/Documentation/vm/overcommit-accounting Buffers uint64 `json:"buffers"` Cached uint64 `json:"cached"` Writeback uint64 `json:"writeback"` From 837fc76f6885d42127f79a0e974eb17ae3cb0a6e Mon Sep 17 00:00:00 2001 From: shirou Date: Sat, 31 Mar 2018 16:55:01 +0900 Subject: [PATCH 36/62] [disk] darwin i386: add definition for darwin/386 and change to use Statfs. it is same as freebsd/openbsd. --- disk/disk_darwin.go | 8 +++---- disk/disk_darwin_386.go | 59 +++++++++++++++++++++++++++++++++++++++++++++++ disk/disk_darwin_amd64.go | 2 +- disk/disk_darwin_arm64.go | 2 +- 4 files changed, 65 insertions(+), 6 deletions(-) create mode 100644 disk/disk_darwin_386.go diff --git a/disk/disk_darwin.go b/disk/disk_darwin.go index 82ffacb..f9fd09c 100644 --- a/disk/disk_darwin.go +++ b/disk/disk_darwin.go @@ -22,7 +22,7 @@ func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, erro if err != nil { return ret, err } - fs := make([]Statfs_t, count) + fs := make([]Statfs, count) _, err = Getfsstat(fs, MntWait) for _, stat := range fs { opts := "rw" @@ -92,16 +92,16 @@ func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, erro return ret, nil } -func Getfsstat(buf []Statfs_t, flags int) (n int, err error) { +func Getfsstat(buf []Statfs, flags int) (n int, err error) { return GetfsstatWithContext(context.Background(), buf, flags) } -func GetfsstatWithContext(ctx context.Context, buf []Statfs_t, flags int) (n int, err error) { +func GetfsstatWithContext(ctx context.Context, buf []Statfs, flags int) (n int, err error) { var _p0 unsafe.Pointer var bufsize uintptr if len(buf) > 0 { _p0 = unsafe.Pointer(&buf[0]) - bufsize = unsafe.Sizeof(Statfs_t{}) * uintptr(len(buf)) + bufsize = unsafe.Sizeof(Statfs{}) * uintptr(len(buf)) } r0, _, e1 := unix.Syscall(SYS_GETFSSTAT64, uintptr(_p0), bufsize, uintptr(flags)) n = int(r0) diff --git a/disk/disk_darwin_386.go b/disk/disk_darwin_386.go new file mode 100644 index 0000000..bd83a4a --- /dev/null +++ b/disk/disk_darwin_386.go @@ -0,0 +1,59 @@ +// +build darwin +// +build 386 + +package disk + +const ( + MntWait = 1 + MfsNameLen = 15 /* length of fs type name, not inc. nul */ + MNameLen = 90 /* length of buffer for returned name */ + + MFSTYPENAMELEN = 16 /* length of fs type name including null */ + MAXPATHLEN = 1024 + MNAMELEN = MAXPATHLEN + + SYS_GETFSSTAT64 = 347 +) + +type Fsid struct{ val [2]int32 } /* file system id type */ +type uid_t int32 + +// sys/mount.h +const ( + MntReadOnly = 0x00000001 /* read only filesystem */ + MntSynchronous = 0x00000002 /* filesystem written synchronously */ + MntNoExec = 0x00000004 /* can't exec from filesystem */ + MntNoSuid = 0x00000008 /* don't honor setuid bits on fs */ + MntUnion = 0x00000020 /* union with underlying filesystem */ + MntAsync = 0x00000040 /* filesystem written asynchronously */ + MntSuidDir = 0x00100000 /* special handling of SUID on dirs */ + MntSoftDep = 0x00200000 /* soft updates being done */ + MntNoSymFollow = 0x00400000 /* do not follow symlinks */ + MntGEOMJournal = 0x02000000 /* GEOM journal support enabled */ + MntMultilabel = 0x04000000 /* MAC support for individual objects */ + MntACLs = 0x08000000 /* ACL support enabled */ + MntNoATime = 0x10000000 /* disable update of file access time */ + MntClusterRead = 0x40000000 /* disable cluster read */ + MntClusterWrite = 0x80000000 /* disable cluster write */ + MntNFS4ACLs = 0x00000010 +) + +// https://github.com/golang/go/blob/master/src/syscall/ztypes_darwin_386.go#L82 +type Statfs struct { + Bsize uint32 + Iosize int32 + Blocks uint64 + Bfree uint64 + Bavail uint64 + Files uint64 + Ffree uint64 + Fsid Fsid + Owner uint32 + Type uint32 + Flags uint32 + Fssubtype uint32 + Fstypename [16]int8 + Mntonname [1024]int8 + Mntfromname [1024]int8 + Reserved [8]uint32 +} diff --git a/disk/disk_darwin_amd64.go b/disk/disk_darwin_amd64.go index f58e213..ec40a75 100644 --- a/disk/disk_darwin_amd64.go +++ b/disk/disk_darwin_amd64.go @@ -38,7 +38,7 @@ const ( MntNFS4ACLs = 0x00000010 ) -type Statfs_t struct { +type Statfs struct { Bsize uint32 Iosize int32 Blocks uint64 diff --git a/disk/disk_darwin_arm64.go b/disk/disk_darwin_arm64.go index 52bcf4c..0e3f670 100644 --- a/disk/disk_darwin_arm64.go +++ b/disk/disk_darwin_arm64.go @@ -38,7 +38,7 @@ const ( MntNFS4ACLs = 0x00000010 ) -type Statfs_t struct { +type Statfs struct { Bsize uint32 Iosize int32 Blocks uint64 From 145dca90f7938605ed1b1f4764aa652f91ae02f4 Mon Sep 17 00:00:00 2001 From: shirou Date: Sat, 31 Mar 2018 21:35:53 +0900 Subject: [PATCH 37/62] change to use CommandContext. --- Makefile | 13 ++++++++++--- cpu/cpu_darwin.go | 4 ++-- cpu/cpu_linux.go | 2 +- cpu/cpu_solaris.go | 4 ++-- disk/disk_linux.go | 2 +- docker/docker_linux.go | 4 ++-- host/host_darwin.go | 10 +++++----- host/host_openbsd.go | 4 ++-- host/host_solaris.go | 10 +++++----- internal/common/common.go | 12 ++++++++++-- internal/common/common_darwin.go | 5 +++-- internal/common/common_unix.go | 9 +++++---- load/load_bsd.go | 2 +- load/load_darwin.go | 4 ++-- mem/mem_darwin.go | 2 +- mem/mem_openbsd.go | 2 +- mem/mem_solaris.go | 9 ++++++--- net/net_darwin.go | 6 +++--- net/net_freebsd.go | 2 +- net/net_openbsd.go | 6 +++--- net/net_unix.go | 2 +- process/process_darwin.go | 32 ++++++++++++++++---------------- process/process_freebsd.go | 2 +- process/process_linux.go | 2 +- process/process_openbsd.go | 4 ++-- 25 files changed, 87 insertions(+), 67 deletions(-) diff --git a/Makefile b/Makefile index cd75f78..83dabed 100644 --- a/Makefile +++ b/Makefile @@ -13,14 +13,21 @@ check: ## Check BUILD_FAIL_PATTERN=grep -v "exec format error" | grep "build failed" && exit 1 || exit 0 build_test: ## test only buildable # Supported operating systems - GOOS=linux go test ./... | $(BUILD_FAIL_PATTERN) + GOOS=linux GOARCH=amd64 go test ./... | $(BUILD_FAIL_PATTERN) + GOOS=linux GOARCH=386 go test ./... | $(BUILD_FAIL_PATTERN) + GOOS=linux GOARCH=arm go test ./... | $(BUILD_FAIL_PATTERN) + GOOS=linux GOARCH=arm64 go test ./... | $(BUILD_FAIL_PATTERN) GOOS=freebsd go test ./... | $(BUILD_FAIL_PATTERN) -# GOOS=openbsd go test ./... | $(BUILD_FAIL_PATTERN) CGO_ENABLED=0 GOOS=darwin go test ./... | $(BUILD_FAIL_PATTERN) GOOS=windows go test ./... | $(BUILD_FAIL_PATTERN) # Operating systems supported for building only (not implemented error if used) GOOS=solaris go test ./... | $(BUILD_FAIL_PATTERN) -# GOOS=dragonfly go test ./... | $(BUILD_FAIL_PATTERN) + GOOS=dragonfly go test ./... | $(BUILD_FAIL_PATTERN) GOOS=netbsd go test ./... | $(BUILD_FAIL_PATTERN) + # cross build to OpenBSD not worked since process has "C" +# GOOS=openbsd go test ./... | $(BUILD_FAIL_PATTERN) + +ifeq ($(shell uname -s), Darwin) CGO_ENABLED=1 GOOS=darwin go test ./... | $(BUILD_FAIL_PATTERN) +endif @echo 'Successfully built on all known operating systems' diff --git a/cpu/cpu_darwin.go b/cpu/cpu_darwin.go index 001517e..74d2737 100644 --- a/cpu/cpu_darwin.go +++ b/cpu/cpu_darwin.go @@ -45,7 +45,7 @@ func InfoWithContext(ctx context.Context) ([]InfoStat, error) { if err != nil { return ret, err } - out, err := invoke.Command(sysctl, "machdep.cpu") + out, err := invoke.CommandWithContext(ctx, sysctl, "machdep.cpu") if err != nil { return ret, err } @@ -99,7 +99,7 @@ func InfoWithContext(ctx context.Context) ([]InfoStat, error) { // Use the rated frequency of the CPU. This is a static value and does not // account for low power or Turbo Boost modes. - out, err = invoke.Command(sysctl, "hw.cpufrequency") + out, err = invoke.CommandWithContext(ctx, sysctl, "hw.cpufrequency") if err != nil { return ret, err } diff --git a/cpu/cpu_linux.go b/cpu/cpu_linux.go index 0f3d928..23b0952 100644 --- a/cpu/cpu_linux.go +++ b/cpu/cpu_linux.go @@ -20,7 +20,7 @@ func init() { if err != nil { return } - out, err := invoke.Command(getconf, "CLK_TCK") + out, err := invoke.CommandWithContext(context.Background(), getconf, "CLK_TCK") // ignore errors if err == nil { i, err := strconv.ParseFloat(strings.TrimSpace(string(out)), 64) diff --git a/cpu/cpu_solaris.go b/cpu/cpu_solaris.go index 0899f41..117fd90 100644 --- a/cpu/cpu_solaris.go +++ b/cpu/cpu_solaris.go @@ -47,7 +47,7 @@ func InfoWithContext(ctx context.Context) ([]InfoStat, error) { if err != nil { return nil, fmt.Errorf("cannot find psrinfo: %s", err) } - psrInfoOut, err := invoke.Command(psrInfo, "-p", "-v") + psrInfoOut, err := invoke.CommandWithContext(ctx, psrInfo, "-p", "-v") if err != nil { return nil, fmt.Errorf("cannot execute psrinfo: %s", err) } @@ -56,7 +56,7 @@ func InfoWithContext(ctx context.Context) ([]InfoStat, error) { if err != nil { return nil, fmt.Errorf("cannot find isainfo: %s", err) } - isaInfoOut, err := invoke.Command(isaInfo, "-b", "-v") + isaInfoOut, err := invoke.CommandWithContext(ctx, isaInfo, "-b", "-v") if err != nil { return nil, fmt.Errorf("cannot execute isainfo: %s", err) } diff --git a/disk/disk_linux.go b/disk/disk_linux.go index f5eb526..46904e3 100644 --- a/disk/disk_linux.go +++ b/disk/disk_linux.go @@ -388,7 +388,7 @@ func GetDiskSerialNumberWithContext(ctx context.Context, name string) string { return "" } - out, err := invoke.Command(udevadm, "info", "--query=property", n) + out, err := invoke.CommandWithContext(ctx, udevadm, "info", "--query=property", n) // does not return error, just an empty string if err != nil { diff --git a/docker/docker_linux.go b/docker/docker_linux.go index ef6cae0..4dcb477 100644 --- a/docker/docker_linux.go +++ b/docker/docker_linux.go @@ -27,7 +27,7 @@ func GetDockerStatWithContext(ctx context.Context) ([]CgroupDockerStat, error) { return nil, ErrDockerNotAvailable } - out, err := invoke.Command(path, "ps", "-a", "--no-trunc", "--format", "{{.ID}}|{{.Image}}|{{.Names}}|{{.Status}}") + out, err := invoke.CommandWithContext(ctx, path, "ps", "-a", "--no-trunc", "--format", "{{.ID}}|{{.Image}}|{{.Names}}|{{.Status}}") if err != nil { return []CgroupDockerStat{}, err } @@ -68,7 +68,7 @@ func GetDockerIDListWithContext(ctx context.Context) ([]string, error) { return nil, ErrDockerNotAvailable } - out, err := invoke.Command(path, "ps", "-q", "--no-trunc") + out, err := invoke.CommandWithContext(ctx, path, "ps", "-q", "--no-trunc") if err != nil { return []string{}, err } diff --git a/host/host_darwin.go b/host/host_darwin.go index c82d990..acefc2f 100644 --- a/host/host_darwin.go +++ b/host/host_darwin.go @@ -40,7 +40,7 @@ func InfoWithContext(ctx context.Context) (*InfoStat, error) { uname, err := exec.LookPath("uname") if err == nil { - out, err := invoke.Command(uname, "-r") + out, err := invoke.CommandWithContext(ctx, uname, "-r") if err == nil { ret.KernelVersion = strings.ToLower(strings.TrimSpace(string(out))) } @@ -70,7 +70,7 @@ func InfoWithContext(ctx context.Context) (*InfoStat, error) { ret.Procs = uint64(len(procs)) } - values, err := common.DoSysctrl("kern.uuid") + values, err := common.DoSysctrlWithContext(ctx, "kern.uuid") if err == nil && len(values) == 1 && values[0] != "" { ret.HostID = strings.ToLower(values[0]) } @@ -90,7 +90,7 @@ func BootTimeWithContext(ctx context.Context) (uint64, error) { if t != 0 { return t, nil } - values, err := common.DoSysctrl("kern.boottime") + values, err := common.DoSysctrlWithContext(ctx, "kern.boottime") if err != nil { return 0, err } @@ -188,12 +188,12 @@ func PlatformInformationWithContext(ctx context.Context) (string, string, string return "", "", "", err } - out, err := invoke.Command(uname, "-s") + out, err := invoke.CommandWithContext(ctx, uname, "-s") if err == nil { platform = strings.ToLower(strings.TrimSpace(string(out))) } - out, err = invoke.Command(sw_vers, "-productVersion") + out, err = invoke.CommandWithContext(ctx, sw_vers, "-productVersion") if err == nil { pver = strings.ToLower(strings.TrimSpace(string(out))) } diff --git a/host/host_openbsd.go b/host/host_openbsd.go index 83c2f7d..2ad64d7 100644 --- a/host/host_openbsd.go +++ b/host/host_openbsd.go @@ -113,12 +113,12 @@ func PlatformInformationWithContext(ctx context.Context) (string, string, string return "", "", "", err } - out, err := invoke.Command(uname, "-s") + out, err := invoke.CommandWithContext(ctx, uname, "-s") if err == nil { platform = strings.ToLower(strings.TrimSpace(string(out))) } - out, err = invoke.Command(uname, "-r") + out, err = invoke.CommandWithContext(ctx, uname, "-r") if err == nil { version = strings.ToLower(strings.TrimSpace(string(out))) } diff --git a/host/host_solaris.go b/host/host_solaris.go index d5657db..bb83bfc 100644 --- a/host/host_solaris.go +++ b/host/host_solaris.go @@ -38,7 +38,7 @@ func InfoWithContext(ctx context.Context) (*InfoStat, error) { return nil, err } - out, err := invoke.Command(uname, "-srv") + out, err := invoke.CommandWithContext(ctx, uname, "-srv") if err != nil { return nil, err } @@ -87,7 +87,7 @@ func InfoWithContext(ctx context.Context) (*InfoStat, error) { // If everything works, use the current zone ID as the HostID if present. zonename, err := exec.LookPath("/usr/bin/zonename") if err == nil { - out, err := invoke.Command(zonename) + out, err := invoke.CommandWithContext(ctx, zonename) if err == nil { sc := bufio.NewScanner(bytes.NewReader(out)) for sc.Scan() { @@ -114,7 +114,7 @@ func InfoWithContext(ctx context.Context) (*InfoStat, error) { if result.HostID == "" { hostID, err := exec.LookPath("/usr/bin/hostid") if err == nil { - out, err := invoke.Command(hostID) + out, err := invoke.CommandWithContext(ctx, hostID) if err == nil { sc := bufio.NewScanner(bytes.NewReader(out)) for sc.Scan() { @@ -156,7 +156,7 @@ func BootTimeWithContext(ctx context.Context) (uint64, error) { return 0, err } - out, err := invoke.Command(kstat, "-p", "unix:0:system_misc:boot_time") + out, err := invoke.CommandWithContext(ctx, kstat, "-p", "unix:0:system_misc:boot_time") if err != nil { return 0, err } @@ -220,7 +220,7 @@ func KernelVersionWithContext(ctx context.Context) (string, error) { return "", err } - out, err := invoke.Command(uname, "-srv") + out, err := invoke.CommandWithContext(ctx, uname, "-srv") if err != nil { return "", err } diff --git a/internal/common/common.go b/internal/common/common.go index fcee6be..f9373ee 100644 --- a/internal/common/common.go +++ b/internal/common/common.go @@ -32,15 +32,19 @@ var ( type Invoker interface { Command(string, ...string) ([]byte, error) + CommandWithContext(context.Context, string, ...string) ([]byte, error) } type Invoke struct{} func (i Invoke) Command(name string, arg ...string) ([]byte, error) { - ctxt, cancel := context.WithTimeout(context.Background(), Timeout) + ctx, cancel := context.WithTimeout(context.Background(), Timeout) defer cancel() + return i.CommandWithContext(ctx, name, arg...) +} - cmd := exec.CommandContext(ctxt, name, arg...) +func (i Invoke) CommandWithContext(ctx context.Context, name string, arg ...string) ([]byte, error) { + cmd := exec.CommandContext(ctx, name, arg...) var buf bytes.Buffer cmd.Stdout = &buf @@ -84,6 +88,10 @@ func (i FakeInvoke) Command(name string, arg ...string) ([]byte, error) { return []byte{}, fmt.Errorf("could not find testdata: %s", fpath) } +func (i FakeInvoke) CommandWithContext(ctx context.Context, name string, arg ...string) ([]byte, error) { + return i.Command(name, arg...) +} + var ErrNotImplementedError = errors.New("not implemented yet") // ReadLines reads contents from a file and splits them by new lines. diff --git a/internal/common/common_darwin.go b/internal/common/common_darwin.go index 2b6d4c1..3e85cc0 100644 --- a/internal/common/common_darwin.go +++ b/internal/common/common_darwin.go @@ -3,6 +3,7 @@ package common import ( + "context" "os" "os/exec" "strings" @@ -11,12 +12,12 @@ import ( "golang.org/x/sys/unix" ) -func DoSysctrl(mib string) ([]string, error) { +func DoSysctrlWithContext(ctx context.Context, mib string) ([]string, error) { sysctl, err := exec.LookPath("/usr/sbin/sysctl") if err != nil { return []string{}, err } - cmd := exec.Command(sysctl, "-n", mib) + cmd := exec.CommandContext(ctx, sysctl, "-n", mib) cmd.Env = getSysctrlEnv(os.Environ()) out, err := cmd.Output() if err != nil { diff --git a/internal/common/common_unix.go b/internal/common/common_unix.go index cc934dd..750a592 100644 --- a/internal/common/common_unix.go +++ b/internal/common/common_unix.go @@ -3,12 +3,13 @@ package common import ( + "context" "os/exec" "strconv" "strings" ) -func CallLsof(invoke Invoker, pid int32, args ...string) ([]string, error) { +func CallLsofWithContext(ctx context.Context, invoke Invoker, pid int32, args ...string) ([]string, error) { var cmd []string if pid == 0 { // will get from all processes. cmd = []string{"-a", "-n", "-P"} @@ -20,7 +21,7 @@ func CallLsof(invoke Invoker, pid int32, args ...string) ([]string, error) { if err != nil { return []string{}, err } - out, err := invoke.Command(lsof, cmd...) + out, err := invoke.CommandWithContext(ctx, lsof, cmd...) if err != nil { // if no pid found, lsof returnes code 1. if err.Error() == "exit status 1" && len(out) == 0 { @@ -39,14 +40,14 @@ func CallLsof(invoke Invoker, pid int32, args ...string) ([]string, error) { return ret, nil } -func CallPgrep(invoke Invoker, pid int32) ([]int32, error) { +func CallPgrepWithContext(ctx context.Context, invoke Invoker, pid int32) ([]int32, error) { var cmd []string cmd = []string{"-P", strconv.Itoa(int(pid))} pgrep, err := exec.LookPath("pgrep") if err != nil { return []int32{}, err } - out, err := invoke.Command(pgrep, cmd...) + out, err := invoke.CommandWithContext(ctx, pgrep, cmd...) if err != nil { return []int32{}, err } diff --git a/load/load_bsd.go b/load/load_bsd.go index cf524ef..dfac10c 100644 --- a/load/load_bsd.go +++ b/load/load_bsd.go @@ -49,7 +49,7 @@ func MiscWithContext(ctx context.Context) (*MiscStat, error) { if err != nil { return nil, err } - out, err := invoke.Command(bin, "axo", "state") + out, err := invoke.CommandWithContext(ctx, bin, "axo", "state") if err != nil { return nil, err } diff --git a/load/load_darwin.go b/load/load_darwin.go index 50f626c..cd7b74d 100644 --- a/load/load_darwin.go +++ b/load/load_darwin.go @@ -16,7 +16,7 @@ func Avg() (*AvgStat, error) { } func AvgWithContext(ctx context.Context) (*AvgStat, error) { - values, err := common.DoSysctrl("vm.loadavg") + values, err := common.DoSysctrlWithContext(ctx, "vm.loadavg") if err != nil { return nil, err } @@ -56,7 +56,7 @@ func MiscWithContext(ctx context.Context) (*MiscStat, error) { if err != nil { return nil, err } - out, err := invoke.Command(bin, "axo", "state") + out, err := invoke.CommandWithContext(ctx, bin, "axo", "state") if err != nil { return nil, err } diff --git a/mem/mem_darwin.go b/mem/mem_darwin.go index 3e259a0..4fe7009 100644 --- a/mem/mem_darwin.go +++ b/mem/mem_darwin.go @@ -35,7 +35,7 @@ func SwapMemory() (*SwapMemoryStat, error) { func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) { var ret *SwapMemoryStat - swapUsage, err := common.DoSysctrl("vm.swapusage") + swapUsage, err := common.DoSysctrlWithContext(ctx, "vm.swapusage") if err != nil { return ret, err } diff --git a/mem/mem_openbsd.go b/mem/mem_openbsd.go index e4834f3..35472a3 100644 --- a/mem/mem_openbsd.go +++ b/mem/mem_openbsd.go @@ -99,7 +99,7 @@ func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) { return nil, err } - out, err := invoke.Command(swapctl, "-sk") + out, err := invoke.CommandWithContext(ctx, swapctl, "-sk") if err != nil { return &SwapMemoryStat{}, nil } diff --git a/mem/mem_solaris.go b/mem/mem_solaris.go index d6c6a5f..0736bc4 100644 --- a/mem/mem_solaris.go +++ b/mem/mem_solaris.go @@ -57,7 +57,8 @@ func zoneName() (string, error) { return "", err } - out, err := invoke.Command(zonename) + ctx := context.Background() + out, err := invoke.CommandWithContext(ctx, zonename) if err != nil { return "", err } @@ -73,7 +74,8 @@ func globalZoneMemoryCapacity() (uint64, error) { return 0, err } - out, err := invoke.Command(prtconf) + ctx := context.Background() + out, err := invoke.CommandWithContext(ctx, prtconf) if err != nil { return 0, err } @@ -99,7 +101,8 @@ func nonGlobalZoneMemoryCapacity() (uint64, error) { return 0, err } - out, err := invoke.Command(kstat, "-p", "-c", "zone_memory_cap", "memory_cap:*:*:physcap") + ctx := context.Background() + out, err := invoke.CommandWithContext(ctx, kstat, "-p", "-c", "zone_memory_cap", "memory_cap:*:*:physcap") if err != nil { return 0, err } diff --git a/net/net_darwin.go b/net/net_darwin.go index 2afb0f0..0d89280 100644 --- a/net/net_darwin.go +++ b/net/net_darwin.go @@ -180,7 +180,7 @@ func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, } // try to get all interface metrics, and hope there won't be any truncated - out, err := invoke.Command(netstat, "-ibdnW") + out, err := invoke.CommandWithContext(ctx, netstat, "-ibdnW") if err != nil { return nil, err } @@ -208,7 +208,7 @@ func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, if err != nil { return nil, err } - if out, err = invoke.Command(ifconfig, "-l"); err != nil { + if out, err = invoke.CommandWithContext(ctx, ifconfig, "-l"); err != nil { return nil, err } interfaceNames := strings.Fields(strings.TrimRight(string(out), endOfLine)) @@ -227,7 +227,7 @@ func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, } if truncated { // run netstat with -I$ifacename - if out, err = invoke.Command(netstat, "-ibdnWI"+interfaceName); err != nil { + if out, err = invoke.CommandWithContext(ctx, netstat, "-ibdnWI"+interfaceName); err != nil { return nil, err } parsedIfaces, err := parseNetstatOutput(string(out)) diff --git a/net/net_freebsd.go b/net/net_freebsd.go index 9daed8d..ce02415 100644 --- a/net/net_freebsd.go +++ b/net/net_freebsd.go @@ -21,7 +21,7 @@ func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, if err != nil { return nil, err } - out, err := invoke.Command(netstat, "-ibdnW") + out, err := invoke.CommandWithContext(ctx, netstat, "-ibdnW") if err != nil { return nil, err } diff --git a/net/net_openbsd.go b/net/net_openbsd.go index 4b194eb..3e74e8f 100644 --- a/net/net_openbsd.go +++ b/net/net_openbsd.go @@ -106,11 +106,11 @@ func IOCountersWithContext(ctx context.Context, pernic bool) ([]IOCountersStat, if err != nil { return nil, err } - out, err := invoke.Command(netstat, "-inb") + out, err := invoke.CommandWithContext(ctx, netstat, "-inb") if err != nil { return nil, err } - out2, err := invoke.Command(netstat, "-ind") + out2, err := invoke.CommandWithContext(ctx, netstat, "-ind") if err != nil { return nil, err } @@ -290,7 +290,7 @@ func ConnectionsWithContext(ctx context.Context, kind string) ([]ConnectionStat, if err != nil { return nil, err } - out, err := invoke.Command(netstat, args...) + out, err := invoke.CommandWithContext(ctx, netstat, args...) if err != nil { return nil, err diff --git a/net/net_unix.go b/net/net_unix.go index 5ceb9cc..e3ff262 100644 --- a/net/net_unix.go +++ b/net/net_unix.go @@ -66,7 +66,7 @@ func ConnectionsPidWithContext(ctx context.Context, kind string, pid int32) ([]C return ret, common.ErrNotImplementedError } - r, err := common.CallLsof(invoke, pid, args...) + r, err := common.CallLsofWithContext(ctx, invoke, pid, args...) if err != nil { return nil, err } diff --git a/process/process_darwin.go b/process/process_darwin.go index 588640a..c949154 100644 --- a/process/process_darwin.go +++ b/process/process_darwin.go @@ -51,7 +51,7 @@ func Pids() ([]int32, error) { func PidsWithContext(ctx context.Context) ([]int32, error) { var ret []int32 - pids, err := callPs("pid", 0, false) + pids, err := callPsWithContext(ctx, "pid", 0, false) if err != nil { return ret, err } @@ -72,7 +72,7 @@ func (p *Process) Ppid() (int32, error) { } func (p *Process) PpidWithContext(ctx context.Context) (int32, error) { - r, err := callPs("ppid", p.Pid, false) + r, err := callPsWithContext(ctx, "ppid", p.Pid, false) if err != nil { return 0, err } @@ -119,9 +119,9 @@ func (p *Process) ExeWithContext(ctx context.Context) (string, error) { return "", err } - lsof := exec.Command(lsof_bin, "-p", strconv.Itoa(int(p.Pid)), "-Fpfn") - awk := exec.Command(awk_bin, "NR==5{print}") - sed := exec.Command(sed_bin, "s/n\\//\\//") + lsof := exec.CommandContext(ctx, lsof_bin, "-p", strconv.Itoa(int(p.Pid)), "-Fpfn") + awk := exec.CommandContext(ctx, awk_bin, "NR==5{print}") + sed := exec.CommandContext(ctx, sed_bin, "s/n\\//\\//") output, _, err := common.Pipeline(lsof, awk, sed) @@ -141,7 +141,7 @@ func (p *Process) Cmdline() (string, error) { } func (p *Process) CmdlineWithContext(ctx context.Context) (string, error) { - r, err := callPs("command", p.Pid, false) + r, err := callPsWithContext(ctx, "command", p.Pid, false) if err != nil { return "", err } @@ -158,7 +158,7 @@ func (p *Process) CmdlineSlice() ([]string, error) { } func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) { - r, err := callPs("command", p.Pid, false) + r, err := callPsWithContext(ctx, "command", p.Pid, false) if err != nil { return nil, err } @@ -169,7 +169,7 @@ func (p *Process) CreateTime() (int64, error) { } func (p *Process) CreateTimeWithContext(ctx context.Context) (int64, error) { - r, err := callPs("etime", p.Pid, false) + r, err := callPsWithContext(ctx, "etime", p.Pid, false) if err != nil { return 0, err } @@ -210,7 +210,7 @@ func (p *Process) Parent() (*Process, error) { } func (p *Process) ParentWithContext(ctx context.Context) (*Process, error) { - rr, err := common.CallLsof(invoke, p.Pid, "-FR") + rr, err := common.CallLsofWithContext(ctx, invoke, p.Pid, "-FR") if err != nil { return nil, err } @@ -232,7 +232,7 @@ func (p *Process) Status() (string, error) { } func (p *Process) StatusWithContext(ctx context.Context) (string, error) { - r, err := callPs("state", p.Pid, false) + r, err := callPsWithContext(ctx, "state", p.Pid, false) if err != nil { return "", err } @@ -350,7 +350,7 @@ func (p *Process) NumThreads() (int32, error) { } func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) { - r, err := callPs("utime,stime", p.Pid, true) + r, err := callPsWithContext(ctx, "utime,stime", p.Pid, true) if err != nil { return 0, err } @@ -395,7 +395,7 @@ func (p *Process) Times() (*cpu.TimesStat, error) { } func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) { - r, err := callPs("utime,stime", p.Pid, false) + r, err := callPsWithContext(ctx, "utime,stime", p.Pid, false) if err != nil { return nil, err @@ -429,7 +429,7 @@ func (p *Process) MemoryInfo() (*MemoryInfoStat, error) { } func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) { - r, err := callPs("rss,vsize,pagein", p.Pid, false) + r, err := callPsWithContext(ctx, "rss,vsize,pagein", p.Pid, false) if err != nil { return nil, err } @@ -467,7 +467,7 @@ func (p *Process) Children() ([]*Process, error) { } func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) { - pids, err := common.CallPgrep(invoke, p.Pid) + pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid) if err != nil { return nil, err } @@ -609,7 +609,7 @@ func NewProcess(pid int32) (*Process, error) { // Return value deletes Header line(you must not input wrong arg). // And splited by Space. Caller have responsibility to manage. // If passed arg pid is 0, get information from all process. -func callPs(arg string, pid int32, threadOption bool) ([][]string, error) { +func callPsWithContext(ctx context.Context, arg string, pid int32, threadOption bool) ([][]string, error) { bin, err := exec.LookPath("ps") if err != nil { return [][]string{}, err @@ -623,7 +623,7 @@ func callPs(arg string, pid int32, threadOption bool) ([][]string, error) { } else { cmd = []string{"-x", "-o", arg, "-p", strconv.Itoa(int(pid))} } - out, err := invoke.Command(bin, cmd...) + out, err := invoke.CommandWithContext(ctx, bin, cmd...) if err != nil { return [][]string{}, err } diff --git a/process/process_freebsd.go b/process/process_freebsd.go index 13b3d88..af2b3b1 100644 --- a/process/process_freebsd.go +++ b/process/process_freebsd.go @@ -355,7 +355,7 @@ func (p *Process) Children() ([]*Process, error) { } func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) { - pids, err := common.CallPgrep(invoke, p.Pid) + pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid) if err != nil { return nil, err } diff --git a/process/process_linux.go b/process/process_linux.go index 894f268..7ec1bc5 100644 --- a/process/process_linux.go +++ b/process/process_linux.go @@ -462,7 +462,7 @@ func (p *Process) Children() ([]*Process, error) { } func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) { - pids, err := common.CallPgrep(invoke, p.Pid) + pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid) if err != nil { if pids == nil || len(pids) == 0 { return nil, ErrorNoChildren diff --git a/process/process_openbsd.go b/process/process_openbsd.go index 78631ac..b7b2cba 100644 --- a/process/process_openbsd.go +++ b/process/process_openbsd.go @@ -5,6 +5,7 @@ package process import ( "C" "bytes" + "context" "encoding/binary" "strings" "unsafe" @@ -15,7 +16,6 @@ import ( net "github.com/shirou/gopsutil/net" "golang.org/x/sys/unix" ) -import "context" // MemoryInfoExStat is different between OSes type MemoryInfoExStat struct { @@ -345,7 +345,7 @@ func (p *Process) Children() ([]*Process, error) { } func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) { - pids, err := common.CallPgrep(invoke, p.Pid) + pids, err := common.CallPgrepWithContext(ctx, invoke, p.Pid) if err != nil { return nil, err } From fc04d2dd9a512906a2604242b35275179e250eda Mon Sep 17 00:00:00 2001 From: shirou Date: Sat, 31 Mar 2018 22:05:15 +0900 Subject: [PATCH 38/62] [host]linux/mips: copy mips64 definition from mips. It just copy from mips, so it may be corrupt, but I can not check it. If something wrong, please open an issue. --- host/host_linux_mips64.go | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 host/host_linux_mips64.go diff --git a/host/host_linux_mips64.go b/host/host_linux_mips64.go new file mode 100644 index 0000000..b0fca09 --- /dev/null +++ b/host/host_linux_mips64.go @@ -0,0 +1,43 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_linux.go + +package host + +const ( + sizeofPtr = 0x4 + sizeofShort = 0x2 + sizeofInt = 0x4 + sizeofLong = 0x4 + sizeofLongLong = 0x8 + sizeOfUtmp = 0x180 +) + +type ( + _C_short int16 + _C_int int32 + _C_long int32 + _C_long_long int64 +) + +type utmp struct { + Type int16 + Pad_cgo_0 [2]byte + Pid int32 + Line [32]int8 + Id [4]int8 + User [32]int8 + Host [256]int8 + Exit exit_status + Session int32 + Tv timeval + Addr_v6 [4]int32 + X__unused [20]int8 +} +type exit_status struct { + Termination int16 + Exit int16 +} +type timeval struct { + Sec int32 + Usec int32 +} From 1b47825dcbaf777bf44f3697e8085f083bd35241 Mon Sep 17 00:00:00 2001 From: Lomanic Date: Fri, 6 Apr 2018 10:11:58 +0200 Subject: [PATCH 39/62] [host] Fix docker uptime parsing (fix #507) --- host/host_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/host/host_linux.go b/host/host_linux.go index 098d086..0e09f15 100644 --- a/host/host_linux.go +++ b/host/host_linux.go @@ -146,7 +146,7 @@ func BootTimeWithContext(ctx context.Context) (uint64, error) { return 0, fmt.Errorf("wrong uptime format") } f := strings.Fields(lines[0]) - b, err := strconv.ParseInt(f[0], 10, 64) + b, err := strconv.ParseFloat(f[0], 64) if err != nil { return 0, err } From 8c8eeb32d3a0a11288543ab11d3bedc6d52143c7 Mon Sep 17 00:00:00 2001 From: Maddie Zhan Date: Sun, 8 Apr 2018 09:21:08 +0800 Subject: [PATCH 40/62] [host] linux/mips: mips64le definition from mipsle Same as commit fc04d2d, but for mips64le from mipsle definition. It would be nice if anyone can check with the correctness, since the qemu environment for mips64/mips64le can't run Go properly due to this issue: https://github.com/golang/go/issues/15416 --- host/host_linux_mips64le.go | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 host/host_linux_mips64le.go diff --git a/host/host_linux_mips64le.go b/host/host_linux_mips64le.go new file mode 100644 index 0000000..b0fca09 --- /dev/null +++ b/host/host_linux_mips64le.go @@ -0,0 +1,43 @@ +// Created by cgo -godefs - DO NOT EDIT +// cgo -godefs types_linux.go + +package host + +const ( + sizeofPtr = 0x4 + sizeofShort = 0x2 + sizeofInt = 0x4 + sizeofLong = 0x4 + sizeofLongLong = 0x8 + sizeOfUtmp = 0x180 +) + +type ( + _C_short int16 + _C_int int32 + _C_long int32 + _C_long_long int64 +) + +type utmp struct { + Type int16 + Pad_cgo_0 [2]byte + Pid int32 + Line [32]int8 + Id [4]int8 + User [32]int8 + Host [256]int8 + Exit exit_status + Session int32 + Tv timeval + Addr_v6 [4]int32 + X__unused [20]int8 +} +type exit_status struct { + Termination int16 + Exit int16 +} +type timeval struct { + Sec int32 + Usec int32 +} From 28b1d7477eeca0635ac905c527fa5a35aebda8c5 Mon Sep 17 00:00:00 2001 From: Daniel Nelson Date: Thu, 12 Apr 2018 15:33:51 -0700 Subject: [PATCH 41/62] Use uint64 when retrieving vfs.bufspace On some system using a the uint32 function would return the error "cannot allocate memory". --- mem/mem_freebsd.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mem/mem_freebsd.go b/mem/mem_freebsd.go index 1fa880b..2d65f80 100644 --- a/mem/mem_freebsd.go +++ b/mem/mem_freebsd.go @@ -39,7 +39,7 @@ func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) { if err != nil { return nil, err } - buffers, err := unix.SysctlUint32("vfs.bufspace") + buffers, err := unix.SysctlUint64("vfs.bufspace") if err != nil { return nil, err } From 35ed9c11c7cb8247ed77288f948b9846552a4734 Mon Sep 17 00:00:00 2001 From: Ofek Lev Date: Thu, 26 Apr 2018 18:27:15 -0400 Subject: [PATCH 42/62] [disk][linux] add label based on devicemapper name of disk --- disk/disk.go | 1 + disk/disk_linux.go | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/disk/disk.go b/disk/disk.go index 1c31047..38d8a8f 100644 --- a/disk/disk.go +++ b/disk/disk.go @@ -42,6 +42,7 @@ type IOCountersStat struct { WeightedIO uint64 `json:"weightedIO"` Name string `json:"name"` SerialNumber string `json:"serialNumber"` + Label string `json:"label"` } func (d UsageStat) String() string { diff --git a/disk/disk_linux.go b/disk/disk_linux.go index 46904e3..852dc1f 100644 --- a/disk/disk_linux.go +++ b/disk/disk_linux.go @@ -5,6 +5,7 @@ package disk import ( "context" "fmt" + "io/ioutil" "os/exec" "path/filepath" "strconv" @@ -370,6 +371,8 @@ func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOC d.Name = name d.SerialNumber = GetDiskSerialNumber(name) + d.Label = GetLabel(name) + ret[name] = d } return ret, nil @@ -406,6 +409,26 @@ func GetDiskSerialNumberWithContext(ctx context.Context, name string) string { return "" } +// GetLabel returns label of given device or empty string on error. +// Name of device is expected, eg. /dev/sda +// Supports label based on devicemapper name +// See https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-block-dm +func GetLabel(name string) string { + // Try label based on devicemapper name + dmname_filename := fmt.Sprintf("/sys/block/%s/dm/name", name) + + if !common.PathExists(dmname_filename) { + return "" + } + + dmname, err := ioutil.ReadFile(dmname_filename) + if err != nil { + return "" + } else { + return dmname + } +} + func getFsType(stat unix.Statfs_t) string { t := int64(stat.Type) ret, ok := fsTypeMap[t] From c95755e4bcd7a62bb8bd33f3a597a7c7f35e2cf3 Mon Sep 17 00:00:00 2001 From: shirou Date: Fri, 27 Apr 2018 10:21:16 +0900 Subject: [PATCH 43/62] [host]darwin: remove GPL code --- host/host_darwin.go | 8 + host/host_darwin_cgo.go | 51 ---- host/host_darwin_nocgo.go | 18 -- host/include/smc.c | 700 ---------------------------------------------- host/include/smc.h | 254 ----------------- 5 files changed, 8 insertions(+), 1023 deletions(-) delete mode 100644 host/host_darwin_cgo.go delete mode 100644 host/host_darwin_nocgo.go delete mode 100644 host/include/smc.c delete mode 100644 host/include/smc.h diff --git a/host/host_darwin.go b/host/host_darwin.go index acefc2f..5a4066a 100644 --- a/host/host_darwin.go +++ b/host/host_darwin.go @@ -217,3 +217,11 @@ func KernelVersionWithContext(ctx context.Context) (string, error) { _, _, version, err := PlatformInformation() return version, err } + +func SensorsTemperatures() ([]TemperatureStat, error) { + return SensorsTemperaturesWithContext(context.Background()) +} + +func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) { + return []TemperatureStat{}, common.ErrNotImplementedError +} diff --git a/host/host_darwin_cgo.go b/host/host_darwin_cgo.go deleted file mode 100644 index f0a4370..0000000 --- a/host/host_darwin_cgo.go +++ /dev/null @@ -1,51 +0,0 @@ -// +build darwin -// +build cgo - -package host - -// #cgo LDFLAGS: -framework IOKit -// #include "include/smc.c" -import "C" -import "context" - -func SensorsTemperatures() ([]TemperatureStat, error) { - return SensorsTemperaturesWithContext(context.Background()) -} - -func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) { - temperatureKeys := []string{ - C.AMBIENT_AIR_0, - C.AMBIENT_AIR_1, - C.CPU_0_DIODE, - C.CPU_0_HEATSINK, - C.CPU_0_PROXIMITY, - C.ENCLOSURE_BASE_0, - C.ENCLOSURE_BASE_1, - C.ENCLOSURE_BASE_2, - C.ENCLOSURE_BASE_3, - C.GPU_0_DIODE, - C.GPU_0_HEATSINK, - C.GPU_0_PROXIMITY, - C.HARD_DRIVE_BAY, - C.MEMORY_SLOT_0, - C.MEMORY_SLOTS_PROXIMITY, - C.NORTHBRIDGE, - C.NORTHBRIDGE_DIODE, - C.NORTHBRIDGE_PROXIMITY, - C.THUNDERBOLT_0, - C.THUNDERBOLT_1, - C.WIRELESS_MODULE, - } - var temperatures []TemperatureStat - - C.open_smc() - defer C.close_smc() - - for _, key := range temperatureKeys { - temperatures = append(temperatures, TemperatureStat{ - SensorKey: key, - Temperature: float64(C.get_tmp(C.CString(key), C.CELSIUS)), - }) - } - return temperatures, nil -} diff --git a/host/host_darwin_nocgo.go b/host/host_darwin_nocgo.go deleted file mode 100644 index 7869f8c..0000000 --- a/host/host_darwin_nocgo.go +++ /dev/null @@ -1,18 +0,0 @@ -// +build darwin -// +build !cgo - -package host - -import ( - "context" - - "github.com/shirou/gopsutil/internal/common" -) - -func SensorsTemperatures() ([]TemperatureStat, error) { - return SensorsTemperaturesWithContext(context.Background()) -} - -func SensorsTemperaturesWithContext(ctx context.Context) ([]TemperatureStat, error) { - return []TemperatureStat{}, common.ErrNotImplementedError -} diff --git a/host/include/smc.c b/host/include/smc.c deleted file mode 100644 index 30a232b..0000000 --- a/host/include/smc.c +++ /dev/null @@ -1,700 +0,0 @@ -/* - * Apple System Management Controller (SMC) API from user space for Intel based - * Macs. Works by talking to the AppleSMC.kext (kernel extension), the driver - * for the SMC. - * - * smc.c - * libsmc - * - * Copyright (C) 2014 beltex - * - * Based off of fork from: - * osx-cpu-temp - * - * With credits to: - * - * Copyright (C) 2006 devnull - * Apple System Management Control (SMC) Tool - * - * Copyright (C) 2006 Hendrik Holtmann - * smcFanControl - * - * This program 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 2 of the License, or - * (at your option) any later version. - * - * This program 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, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include -#include -#include "smc.h" - - -//------------------------------------------------------------------------------ -// MARK: MACROS -//------------------------------------------------------------------------------ - - -/** -Name of the SMC IOService as seen in the IORegistry. You can view it either via -command line with ioreg or through the IORegistryExplorer app (found on Apple's -developer site - Hardware IO Tools for Xcode) -*/ -#define IOSERVICE_SMC "AppleSMC" - - -/** -IOService for getting machine model name -*/ -#define IOSERVICE_MODEL "IOPlatformExpertDevice" - - -/** -SMC data types - 4 byte multi-character constants - -Sources: See TMP SMC keys in smc.h - -http://stackoverflow.com/questions/22160746/fpe2-and-sp78-data-types -*/ -#define DATA_TYPE_UINT8 "ui8 " -#define DATA_TYPE_UINT16 "ui16" -#define DATA_TYPE_UINT32 "ui32" -#define DATA_TYPE_FLAG "flag" -#define DATA_TYPE_FPE2 "fpe2" -#define DATA_TYPE_SFDS "{fds" -#define DATA_TYPE_SP78 "sp78" - - -//------------------------------------------------------------------------------ -// MARK: GLOBAL VARS -//------------------------------------------------------------------------------ - - -/** -Our connection to the SMC -*/ -static io_connect_t conn; - - -/** -Number of characters in an SMC key -*/ -static const int SMC_KEY_SIZE = 4; - - -/** -Number of characters in a data type "key" returned from the SMC. See data type -macros. -*/ -static const int DATA_TYPE_SIZE = 4; - - -//------------------------------------------------------------------------------ -// MARK: ENUMS -//------------------------------------------------------------------------------ - - -/** -Defined by AppleSMC.kext. See SMCParamStruct. - -These are SMC specific return codes -*/ -typedef enum { - kSMCSuccess = 0, - kSMCError = 1, - kSMCKeyNotFound = 0x84 -} kSMC_t; - - -/** -Defined by AppleSMC.kext. See SMCParamStruct. - -Function selectors. Used to tell the SMC which function inside it to call. -*/ -typedef enum { - kSMCUserClientOpen = 0, - kSMCUserClientClose = 1, - kSMCHandleYPCEvent = 2, - kSMCReadKey = 5, - kSMCWriteKey = 6, - kSMCGetKeyCount = 7, - kSMCGetKeyFromIndex = 8, - kSMCGetKeyInfo = 9 -} selector_t; - - -//------------------------------------------------------------------------------ -// MARK: STRUCTS -//------------------------------------------------------------------------------ - - -/** -Defined by AppleSMC.kext. See SMCParamStruct. -*/ -typedef struct { - unsigned char major; - unsigned char minor; - unsigned char build; - unsigned char reserved; - unsigned short release; -} SMCVersion; - - -/** -Defined by AppleSMC.kext. See SMCParamStruct. -*/ -typedef struct { - uint16_t version; - uint16_t length; - uint32_t cpuPLimit; - uint32_t gpuPLimit; - uint32_t memPLimit; -} SMCPLimitData; - - -/** -Defined by AppleSMC.kext. See SMCParamStruct. - -- dataSize : How many values written to SMCParamStruct.bytes -- dataType : Type of data written to SMCParamStruct.bytes. This lets us know how - to interpret it (translate it to human readable) -*/ -typedef struct { - IOByteCount dataSize; - uint32_t dataType; - uint8_t dataAttributes; -} SMCKeyInfoData; - - -/** -Defined by AppleSMC.kext. - -This is the predefined struct that must be passed to communicate with the -AppleSMC driver. While the driver is closed source, the definition of this -struct happened to appear in the Apple PowerManagement project at around -version 211, and soon after disappeared. It can be seen in the PrivateLib.c -file under pmconfigd. - -https://www.opensource.apple.com/source/PowerManagement/PowerManagement-211/ -*/ -typedef struct { - uint32_t key; - SMCVersion vers; - SMCPLimitData pLimitData; - SMCKeyInfoData keyInfo; - uint8_t result; - uint8_t status; - uint8_t data8; - uint32_t data32; - uint8_t bytes[32]; -} SMCParamStruct; - - -/** -Used for returning data from the SMC. -*/ -typedef struct { - uint8_t data[32]; - uint32_t dataType; - uint32_t dataSize; - kSMC_t kSMC; -} smc_return_t; - - -//------------------------------------------------------------------------------ -// MARK: HELPERS - TYPE CONVERSION -//------------------------------------------------------------------------------ - - -/** -Convert data from SMC of fpe2 type to human readable. - -:param: data Data from the SMC to be converted. Assumed data size of 2. -:returns: Converted data -*/ -static unsigned int from_fpe2(uint8_t data[32]) -{ - unsigned int ans = 0; - - // Data type for fan calls - fpe2 - // This is assumend to mean floating point, with 2 exponent bits - // http://stackoverflow.com/questions/22160746/fpe2-and-sp78-data-types - ans += data[0] << 6; - ans += data[1] << 2; - - return ans; -} - - -/** -Convert to fpe2 data type to be passed to SMC. - -:param: val Value to convert -:param: data Pointer to data array to place result -*/ -static void to_fpe2(unsigned int val, uint8_t *data) -{ - data[0] = val >> 6; - data[1] = (val << 2) ^ (data[0] << 8); -} - - -/** -Convert SMC key to uint32_t. This must be done to pass it to the SMC. - -:param: key The SMC key to convert -:returns: uint32_t translation. - Returns zero if key is not 4 characters in length. -*/ -static uint32_t to_uint32_t(char *key) -{ - uint32_t ans = 0; - uint32_t shift = 24; - - // SMC key is expected to be 4 bytes - thus 4 chars - if (strlen(key) != SMC_KEY_SIZE) { - return 0; - } - - for (int i = 0; i < SMC_KEY_SIZE; i++) { - ans += key[i] << shift; - shift -= 8; - } - - return ans; -} - - -/** -For converting the dataType return from the SMC to human readable 4 byte -multi-character constant. -*/ -static void to_string(uint32_t val, char *dataType) -{ - int shift = 24; - - for (int i = 0; i < DATA_TYPE_SIZE; i++) { - // To get each char, we shift it into the lower 8 bits, and then & by - // 255 to insolate it - dataType[i] = (val >> shift) & 0xff; - shift -= 8; - } -} - - -//------------------------------------------------------------------------------ -// MARK: HELPERS - TMP CONVERSION -//------------------------------------------------------------------------------ - - -/** -Celsius to Fahrenheit -*/ -static double to_fahrenheit(double tmp) -{ - // http://en.wikipedia.org/wiki/Fahrenheit#Definition_and_conversions - return (tmp * 1.8) + 32; -} - - -/** -Celsius to Kelvin -*/ -static double to_kelvin(double tmp) -{ - // http://en.wikipedia.org/wiki/Kelvin - return tmp + 273.15; -} - - -//------------------------------------------------------------------------------ -// MARK: "PRIVATE" FUNCTIONS -//------------------------------------------------------------------------------ - - -/** -Make a call to the SMC - -:param: inputStruct Struct that holds data telling the SMC what you want -:param: outputStruct Struct holding the SMC's response -:returns: I/O Kit return code -*/ -static kern_return_t call_smc(SMCParamStruct *inputStruct, - SMCParamStruct *outputStruct) -{ - kern_return_t result; - size_t inputStructCnt = sizeof(SMCParamStruct); - size_t outputStructCnt = sizeof(SMCParamStruct); - - result = IOConnectCallStructMethod(conn, kSMCHandleYPCEvent, - inputStruct, - inputStructCnt, - outputStruct, - &outputStructCnt); - - if (result != kIOReturnSuccess) { - // IOReturn error code lookup. See "Accessing Hardware From Applications - // -> Handling Errors" Apple doc - result = err_get_code(result); - } - - return result; -} - - -/** -Read data from the SMC - -:param: key The SMC key -*/ -static kern_return_t read_smc(char *key, smc_return_t *result_smc) -{ - kern_return_t result; - SMCParamStruct inputStruct; - SMCParamStruct outputStruct; - - memset(&inputStruct, 0, sizeof(SMCParamStruct)); - memset(&outputStruct, 0, sizeof(SMCParamStruct)); - memset(result_smc, 0, sizeof(smc_return_t)); - - // First call to AppleSMC - get key info - inputStruct.key = to_uint32_t(key); - inputStruct.data8 = kSMCGetKeyInfo; - - result = call_smc(&inputStruct, &outputStruct); - result_smc->kSMC = outputStruct.result; - - if (result != kIOReturnSuccess || outputStruct.result != kSMCSuccess) { - return result; - } - - // Store data for return - result_smc->dataSize = outputStruct.keyInfo.dataSize; - result_smc->dataType = outputStruct.keyInfo.dataType; - - - // Second call to AppleSMC - now we can get the data - inputStruct.keyInfo.dataSize = outputStruct.keyInfo.dataSize; - inputStruct.data8 = kSMCReadKey; - - result = call_smc(&inputStruct, &outputStruct); - result_smc->kSMC = outputStruct.result; - - if (result != kIOReturnSuccess || outputStruct.result != kSMCSuccess) { - return result; - } - - memcpy(result_smc->data, outputStruct.bytes, sizeof(outputStruct.bytes)); - - return result; -} - - -/** -Write data to the SMC. - -:returns: IOReturn IOKit return code -*/ -static kern_return_t write_smc(char *key, smc_return_t *result_smc) -{ - kern_return_t result; - SMCParamStruct inputStruct; - SMCParamStruct outputStruct; - - memset(&inputStruct, 0, sizeof(SMCParamStruct)); - memset(&outputStruct, 0, sizeof(SMCParamStruct)); - - // First call to AppleSMC - get key info - inputStruct.key = to_uint32_t(key); - inputStruct.data8 = kSMCGetKeyInfo; - - result = call_smc(&inputStruct, &outputStruct); - result_smc->kSMC = outputStruct.result; - - if (result != kIOReturnSuccess || outputStruct.result != kSMCSuccess) { - return result; - } - - // Check data is correct - if (result_smc->dataSize != outputStruct.keyInfo.dataSize || - result_smc->dataType != outputStruct.keyInfo.dataType) { - return kIOReturnBadArgument; - } - - // Second call to AppleSMC - now we can write the data - inputStruct.data8 = kSMCWriteKey; - inputStruct.keyInfo.dataSize = outputStruct.keyInfo.dataSize; - - // Set data to write - memcpy(inputStruct.bytes, result_smc->data, sizeof(result_smc->data)); - - result = call_smc(&inputStruct, &outputStruct); - result_smc->kSMC = outputStruct.result; - - return result; -} - - -/** -Get the model name of the machine. -*/ -static kern_return_t get_machine_model(io_name_t model) -{ - io_service_t service; - kern_return_t result; - - service = IOServiceGetMatchingService(kIOMasterPortDefault, - IOServiceMatching(IOSERVICE_MODEL)); - - if (service == 0) { - printf("ERROR: %s NOT FOUND\n", IOSERVICE_MODEL); - return kIOReturnError; - } - - // Get the model name - result = IORegistryEntryGetName(service, model); - IOObjectRelease(service); - - return result; -} - - -//------------------------------------------------------------------------------ -// MARK: "PUBLIC" FUNCTIONS -//------------------------------------------------------------------------------ - - -kern_return_t open_smc(void) -{ - kern_return_t result; - io_service_t service; - - service = IOServiceGetMatchingService(kIOMasterPortDefault, - IOServiceMatching(IOSERVICE_SMC)); - - if (service == 0) { - // NOTE: IOServiceMatching documents 0 on failure - printf("ERROR: %s NOT FOUND\n", IOSERVICE_SMC); - return kIOReturnError; - } - - result = IOServiceOpen(service, mach_task_self(), 0, &conn); - IOObjectRelease(service); - - return result; -} - - -kern_return_t close_smc(void) -{ - return IOServiceClose(conn); -} - - -bool is_key_valid(char *key) -{ - bool ans = false; - kern_return_t result; - smc_return_t result_smc; - - if (strlen(key) != SMC_KEY_SIZE) { - printf("ERROR: Invalid key size - must be 4 chars\n"); - return ans; - } - - // Try a read and see if it succeeds - result = read_smc(key, &result_smc); - - if (result == kIOReturnSuccess && result_smc.kSMC == kSMCSuccess) { - ans = true; - } - - return ans; -} - - -double get_tmp(char *key, tmp_unit_t unit) -{ - kern_return_t result; - smc_return_t result_smc; - - result = read_smc(key, &result_smc); - - if (!(result == kIOReturnSuccess && - result_smc.dataSize == 2 && - result_smc.dataType == to_uint32_t(DATA_TYPE_SP78))) { - // Error - return 0.0; - } - - // TODO: Create from_sp78() convert function - double tmp = result_smc.data[0]; - - switch (unit) { - case CELSIUS: - break; - case FAHRENHEIT: - tmp = to_fahrenheit(tmp); - break; - case KELVIN: - tmp = to_kelvin(tmp); - break; - } - - return tmp; -} - - -bool is_battery_powered(void) -{ - kern_return_t result; - smc_return_t result_smc; - - result = read_smc(BATT_PWR, &result_smc); - - if (!(result == kIOReturnSuccess && - result_smc.dataSize == 1 && - result_smc.dataType == to_uint32_t(DATA_TYPE_FLAG))) { - // Error - return false; - } - - return result_smc.data[0]; -} - - -bool is_optical_disk_drive_full(void) -{ - kern_return_t result; - smc_return_t result_smc; - - result = read_smc(ODD_FULL, &result_smc); - - if (!(result == kIOReturnSuccess && - result_smc.dataSize == 1 && - result_smc.dataType == to_uint32_t(DATA_TYPE_FLAG))) { - // Error - return false; - } - - return result_smc.data[0]; -} - - -//------------------------------------------------------------------------------ -// MARK: FAN FUNCTIONS -//------------------------------------------------------------------------------ - - -bool get_fan_name(unsigned int fan_num, fan_name_t name) -{ - char key[5]; - kern_return_t result; - smc_return_t result_smc; - - sprintf(key, "F%dID", fan_num); - result = read_smc(key, &result_smc); - - if (!(result == kIOReturnSuccess && - result_smc.dataSize == 16 && - result_smc.dataType == to_uint32_t(DATA_TYPE_SFDS))) { - return false; - } - - - /* - We know the data size is 16 bytes and the type is "{fds", a custom - struct defined by the AppleSMC.kext. See TMP enum sources for the - struct. - - The last 12 bytes contain the name of the fan, an array of chars, hence - the loop range. - */ - int index = 0; - for (int i = 4; i < 16; i++) { - // Check if at the end (name may not be full 12 bytes) - // Could check for 0 (null), but instead we check for 32 (space). This - // is a hack to remove whitespace. :) - if (result_smc.data[i] == 32) { - break; - } - - name[index] = result_smc.data[i]; - index++; - } - - return true; -} - - -int get_num_fans(void) -{ - kern_return_t result; - smc_return_t result_smc; - - result = read_smc(NUM_FANS, &result_smc); - - if (!(result == kIOReturnSuccess && - result_smc.dataSize == 1 && - result_smc.dataType == to_uint32_t(DATA_TYPE_UINT8))) { - // Error - return -1; - } - - return result_smc.data[0]; -} - - -unsigned int get_fan_rpm(unsigned int fan_num) -{ - char key[5]; - kern_return_t result; - smc_return_t result_smc; - - sprintf(key, "F%dAc", fan_num); - result = read_smc(key, &result_smc); - - if (!(result == kIOReturnSuccess && - result_smc.dataSize == 2 && - result_smc.dataType == to_uint32_t(DATA_TYPE_FPE2))) { - // Error - return 0; - } - - return from_fpe2(result_smc.data); -} - - -bool set_fan_min_rpm(unsigned int fan_num, unsigned int rpm, bool auth) -{ - // TODO: Add rpm val safety check - char key[5]; - bool ans = false; - kern_return_t result; - smc_return_t result_smc; - - memset(&result_smc, 0, sizeof(smc_return_t)); - - // TODO: Don't use magic number - result_smc.dataSize = 2; - result_smc.dataType = to_uint32_t(DATA_TYPE_FPE2); - to_fpe2(rpm, result_smc.data); - - sprintf(key, "F%dMn", fan_num); - result = write_smc(key, &result_smc); - - if (result == kIOReturnSuccess && result_smc.kSMC == kSMCSuccess) { - ans = true; - } - - return ans; -} diff --git a/host/include/smc.h b/host/include/smc.h deleted file mode 100644 index b156368..0000000 --- a/host/include/smc.h +++ /dev/null @@ -1,254 +0,0 @@ -/* - * Apple System Management Controller (SMC) API from user space for Intel based - * Macs. Works by talking to the AppleSMC.kext (kernel extension), the driver - * for the SMC. - * - * smc.h - * libsmc - * - * Copyright (C) 2014 beltex - * - * Based off of fork from: - * osx-cpu-temp - * - * With credits to: - * - * Copyright (C) 2006 devnull - * Apple System Management Control (SMC) Tool - * - * Copyright (C) 2006 Hendrik Holtmann - * smcFanControl - * - * This program 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 2 of the License, or - * (at your option) any later version. - * - * This program 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, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include - - -//------------------------------------------------------------------------------ -// MARK: MACROS -//------------------------------------------------------------------------------ - - -/** -SMC keys for temperature sensors - 4 byte multi-character constants - -Not applicable to all Mac's of course. In adition, the definition of the codes -may not be 100% accurate necessarily. Finally, list is incomplete. - -Presumed letter translations: - -- T = Temperature (if first char) -- C = CPU -- G = GPU -- P = Proximity -- D = Diode -- H = Heatsink - -Sources: - -- https://www.apple.com/downloads/dashboard/status/istatpro.html -- https://github.com/hholtmann/smcFanControl -- https://github.com/jedda/OSX-Monitoring-Tools -- http://www.parhelia.ch/blog/statics/k3_keys.html -*/ -#define AMBIENT_AIR_0 "TA0P" -#define AMBIENT_AIR_1 "TA1P" -#define CPU_0_DIODE "TC0D" -#define CPU_0_HEATSINK "TC0H" -#define CPU_0_PROXIMITY "TC0P" -#define ENCLOSURE_BASE_0 "TB0T" -#define ENCLOSURE_BASE_1 "TB1T" -#define ENCLOSURE_BASE_2 "TB2T" -#define ENCLOSURE_BASE_3 "TB3T" -#define GPU_0_DIODE "TG0D" -#define GPU_0_HEATSINK "TG0H" -#define GPU_0_PROXIMITY "TG0P" -#define HARD_DRIVE_BAY "TH0P" -#define MEMORY_SLOT_0 "TM0S" -#define MEMORY_SLOTS_PROXIMITY "TM0P" -#define NORTHBRIDGE "TN0H" -#define NORTHBRIDGE_DIODE "TN0D" -#define NORTHBRIDGE_PROXIMITY "TN0P" -#define THUNDERBOLT_0 "TI0P" -#define THUNDERBOLT_1 "TI1P" -#define WIRELESS_MODULE "TW0P" - - -/** -SMC keys for fans - 4 byte multi-character constants - -Number of fans on Macs vary of course, thus not all keys will be applicable. - -Presumed letter translations: - -- F = Fan -- Ac = Acutal -- Mn = Min -- Mx = Max -- Sf = Safe -- Tg = Target - -Sources: See TMP SMC keys -*/ -#define FAN_0 "F0Ac" -#define FAN_0_MIN_RPM "F0Mn" -#define FAN_0_MAX_RPM "F0Mx" -#define FAN_0_SAFE_RPM "F0Sf" -#define FAN_0_TARGET_RPM "F0Tg" -#define FAN_1 "F1Ac" -#define FAN_1_MIN_RPM "F1Mn" -#define FAN_1_MAX_RPM "F1Mx" -#define FAN_1_SAFE_RPM "F1Sf" -#define FAN_1_TARGET_RPM "F1Tg" -#define FAN_2 "F2Ac" -#define FAN_2_MIN_RPM "F2Mn" -#define FAN_2_MAX_RPM "F2Mx" -#define FAN_2_SAFE_RPM "F2Sf" -#define FAN_2_TARGET_RPM "F2Tg" -#define NUM_FANS "FNum" -#define FORCE_BITS "FS! " - - -/** -Misc SMC keys - 4 byte multi-character constants - -Sources: See TMP SMC keys -*/ -#define BATT_PWR "BATP" -#define NUM_KEYS "#KEY" -#define ODD_FULL "MSDI" - - -//------------------------------------------------------------------------------ -// MARK: TYPES -//------------------------------------------------------------------------------ - - -typedef char fan_name_t[13]; - - -//------------------------------------------------------------------------------ -// MARK: ENUMS -//------------------------------------------------------------------------------ - - -typedef enum { - CELSIUS, - FAHRENHEIT, - KELVIN -} tmp_unit_t; - - -//------------------------------------------------------------------------------ -// MARK: PROTOTYPES -//------------------------------------------------------------------------------ - - -/** -Open a connection to the SMC - -:returns: kIOReturnSuccess on successful connection to the SMC. -*/ -kern_return_t open_smc(void); - - -/** -Close connection to the SMC - -:returns: kIOReturnSuccess on successful close of connection to the SMC. -*/ -kern_return_t close_smc(void); - - -/** -Check if an SMC key is valid. Useful for determining if a certain machine has -particular sensor or fan for example. - -:param: key The SMC key to check. 4 byte multi-character constant. Must be 4 - characters in length. -:returns: True if the key is found, false otherwise -*/ -bool is_key_valid(char *key); - - -/** -Get the current temperature from a sensor - -:param: key The temperature sensor to read from -:param: unit The unit for the temperature value. -:returns: Temperature of sensor. If the sensor is not found, or an error - occurs, return will be zero -*/ -double get_tmp(char *key, tmp_unit_t unit); - - -/** -Is the machine being powered by the battery? - -:returns: True if it is, false otherwise -*/ -bool is_battery_powered(void); - - -/** -Is there a CD in the optical disk drive (ODD)? - -:returns: True if there is, false otherwise -*/ -bool is_optical_disk_drive_full(void); - - -/** -Get the name of a fan. - -:param: fanNum The number of the fan to check -:param: name The name of the fan. Return will be empty on error. -:returns: True if successful, false otherwise. -*/ -bool get_fan_name(unsigned int fan_num, fan_name_t name); - - -/** -Get the number of fans on this machine. - -:returns: The number of fans. If an error occurs, return will be -1. -*/ -int get_num_fans(void); - - -/** -Get the current speed (RPM - revolutions per minute) of a fan. - -:param: fan_num The number of the fan to check -:returns: The fan RPM. If the fan is not found, or an error occurs, return - will be zero -*/ -UInt get_fan_rpm(UInt fan_num); - - -/** -Set the minimum speed (RPM - revolutions per minute) of a fan. This method -requires root privileges. By minimum we mean that OS X can interject and -raise the fan speed if needed, however it will not go below this. - -WARNING: You are playing with hardware here, BE CAREFUL. - -:param: fan_num The number of the fan to set -:param: rpm The speed you would like to set the fan to. -:param: auth Should the function do authentication? -:return: True if successful, false otherwise -*/ -bool set_fan_min_rpm(unsigned int fan_num, unsigned int rpm, bool auth); From 0f8edaa97ee4c3f0028af3ad768a1d40fe7f2fb3 Mon Sep 17 00:00:00 2001 From: Ofek Lev Date: Sun, 29 Apr 2018 21:51:27 -0400 Subject: [PATCH 44/62] address review --- README.rst | 4 ++++ disk/disk_linux.go | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index bc4614f..9848aec 100644 --- a/README.rst +++ b/README.rst @@ -117,6 +117,10 @@ Several methods have been added which are not present in psutil, but will provid - VirtualizationSystem (ex: "LXC") - VirtualizationRole (ex: "guest"/"host") +- IOCounters + + - Label (linux only) The registered [device mapper name](https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-block-dm) + - cpu/CPUInfo() (linux, freebsd) - CPU (ex: 0, 1, ...) diff --git a/disk/disk_linux.go b/disk/disk_linux.go index 852dc1f..8ca7ab6 100644 --- a/disk/disk_linux.go +++ b/disk/disk_linux.go @@ -415,7 +415,7 @@ func GetDiskSerialNumberWithContext(ctx context.Context, name string) string { // See https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-block-dm func GetLabel(name string) string { // Try label based on devicemapper name - dmname_filename := fmt.Sprintf("/sys/block/%s/dm/name", name) + dmname_filename := common.HostSys(fmt.Sprintf("block/%s/dm/name", name)) if !common.PathExists(dmname_filename) { return "" From 6a55ce09587706bfa565e71b3430d2e213c35e75 Mon Sep 17 00:00:00 2001 From: shirou Date: Wed, 2 May 2018 14:46:44 +0900 Subject: [PATCH 45/62] [disk]linux: fix Getlabel --- disk/disk_linux.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/disk/disk_linux.go b/disk/disk_linux.go index 8ca7ab6..4033b1a 100644 --- a/disk/disk_linux.go +++ b/disk/disk_linux.go @@ -425,7 +425,7 @@ func GetLabel(name string) string { if err != nil { return "" } else { - return dmname + return string(dmname) } } From 57f370e13068146efe1cb7129f79e5d51da8a242 Mon Sep 17 00:00:00 2001 From: shirou Date: Wed, 2 May 2018 14:58:36 +0900 Subject: [PATCH 46/62] [disk]linux: fix test --- disk/disk_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/disk/disk_test.go b/disk/disk_test.go index 269ca6c..a0251cb 100644 --- a/disk/disk_test.go +++ b/disk/disk_test.go @@ -96,7 +96,7 @@ func TestDiskIOCountersStat_String(t *testing.T) { WriteBytes: 400, SerialNumber: "SERIAL", } - e := `{"readCount":100,"mergedReadCount":0,"writeCount":200,"mergedWriteCount":0,"readBytes":300,"writeBytes":400,"readTime":0,"writeTime":0,"iopsInProgress":0,"ioTime":0,"weightedIO":0,"name":"sd01","serialNumber":"SERIAL"}` + e := `{"readCount":100,"mergedReadCount":0,"writeCount":200,"mergedWriteCount":0,"readBytes":300,"writeBytes":400,"readTime":0,"writeTime":0,"iopsInProgress":0,"ioTime":0,"weightedIO":0,"name":"sd01","serialNumber":"SERIAL","label":""}` if e != fmt.Sprintf("%v", v) { t.Errorf("DiskUsageStat string is invalid: %v", v) } From 0f1576d63c939651ce16e972dc444a2f7017a34d Mon Sep 17 00:00:00 2001 From: shirou Date: Sat, 19 May 2018 09:25:20 +0900 Subject: [PATCH 47/62] mem[windows]: if no swap, total and/or used is 0. --- mem/mem_windows.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mem/mem_windows.go b/mem/mem_windows.go index cc9ce82..6b64ed0 100644 --- a/mem/mem_windows.go +++ b/mem/mem_windows.go @@ -4,6 +4,7 @@ package mem import ( "context" + "errors" "unsafe" "github.com/shirou/gopsutil/internal/common" @@ -79,6 +80,10 @@ func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) { } tot := perfInfo.commitLimit * perfInfo.pageSize used := perfInfo.commitTotal * perfInfo.pageSize + if tot == 0 || used == 0 { + return nil, errors.New("total or used memory is 0") + } + free := tot - used ret := &SwapMemoryStat{ Total: tot, From 3adcafe5fd6204958f1ffd46f2891fa551fd088d Mon Sep 17 00:00:00 2001 From: Steven Lee Date: Sat, 19 May 2018 10:45:38 +0800 Subject: [PATCH 48/62] mem[windows]: if no swap, just return 0 used percent default --- mem/mem_windows.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/mem/mem_windows.go b/mem/mem_windows.go index 6b64ed0..cfdf8bd 100644 --- a/mem/mem_windows.go +++ b/mem/mem_windows.go @@ -4,7 +4,6 @@ package mem import ( "context" - "errors" "unsafe" "github.com/shirou/gopsutil/internal/common" @@ -80,16 +79,18 @@ func SwapMemoryWithContext(ctx context.Context) (*SwapMemoryStat, error) { } tot := perfInfo.commitLimit * perfInfo.pageSize used := perfInfo.commitTotal * perfInfo.pageSize - if tot == 0 || used == 0 { - return nil, errors.New("total or used memory is 0") - } - free := tot - used + var usedPercent float64 + if tot == 0 { + usedPercent = 0 + } else { + usedPercent = float64(used) / float64(tot) + } ret := &SwapMemoryStat{ Total: tot, Used: used, Free: free, - UsedPercent: float64(used) / float64(tot), + UsedPercent: usedPercent, } return ret, nil From 038eb0628885d8f68ed0eb83850cc2749d84cc79 Mon Sep 17 00:00:00 2001 From: Ofek Lev Date: Thu, 24 May 2018 02:12:10 -0400 Subject: [PATCH 49/62] fix link --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 9848aec..63f5f69 100644 --- a/README.rst +++ b/README.rst @@ -119,7 +119,7 @@ Several methods have been added which are not present in psutil, but will provid - IOCounters - - Label (linux only) The registered [device mapper name](https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-block-dm) + - Label (linux only) The registered `device mapper name `_ - cpu/CPUInfo() (linux, freebsd) From f03124bf8618adfc75dd87ff7d95df29fd309e42 Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Fri, 1 Jun 2018 13:57:14 +0200 Subject: [PATCH 50/62] Read disk model and serial from udev data or sysfs on Linux It reads model and serial data directly from files instead of using `udevadm` command. This way obtaining the disk serial number doesn't depend on command execution, and can be also possible even if udev or udevadm are not available. --- disk/disk_linux.go | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/disk/disk_linux.go b/disk/disk_linux.go index 4033b1a..2e86147 100644 --- a/disk/disk_linux.go +++ b/disk/disk_linux.go @@ -3,10 +3,11 @@ package disk import ( + "bufio" + "bytes" "context" "fmt" "io/ioutil" - "os/exec" "path/filepath" "strconv" "strings" @@ -385,26 +386,33 @@ func GetDiskSerialNumber(name string) string { } func GetDiskSerialNumberWithContext(ctx context.Context, name string) string { - n := fmt.Sprintf("--name=%s", name) - udevadm, err := exec.LookPath("/sbin/udevadm") + var stat unix.Stat_t + err := unix.Stat(name, &stat) if err != nil { return "" } + major := unix.Major(stat.Rdev) + minor := unix.Minor(stat.Rdev) - out, err := invoke.CommandWithContext(ctx, udevadm, "info", "--query=property", n) - - // does not return error, just an empty string - if err != nil { - return "" - } - lines := strings.Split(string(out), "\n") - for _, line := range lines { - values := strings.Split(line, "=") - if len(values) < 2 || values[0] != "ID_SERIAL" { - // only get ID_SERIAL, not ID_SERIAL_SHORT - continue + // Try to get the serial from udev data + udevDataPath := fmt.Sprintf("/run/udev/data/b%d:%d", major, minor) + if udevdata, err := ioutil.ReadFile(udevDataPath); err == nil { + scanner := bufio.NewScanner(bytes.NewReader(udevdata)) + for scanner.Scan() { + values := strings.Split(scanner.Text(), "=") + if len(values) == 2 && values[0] == "E:ID_SERIAL" { + return values[1] + } } - return values[1] + } + + // Try to get the serial from sysfs, look at the disk device (minor 0) directly + // because if it is a partition it is not going to contain any device information + devicePath := fmt.Sprintf("/sys/dev/block/%d:0/device", major) + model, _ := ioutil.ReadFile(filepath.Join(devicePath, "model")) + serial, _ := ioutil.ReadFile(filepath.Join(devicePath, "serial")) + if len(model) > 0 && len(serial) > 0 { + return fmt.Sprintf("%s_%s", string(model), string(serial)) } return "" } From d813235a11fab408bfa217f32777322ae47090f8 Mon Sep 17 00:00:00 2001 From: shirou Date: Tue, 12 Jun 2018 22:59:43 +0900 Subject: [PATCH 51/62] disk[linux]: cast uint64 for build on MIPS related: #533 --- disk/disk_linux.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/disk/disk_linux.go b/disk/disk_linux.go index 2e86147..6d0ca96 100644 --- a/disk/disk_linux.go +++ b/disk/disk_linux.go @@ -391,8 +391,8 @@ func GetDiskSerialNumberWithContext(ctx context.Context, name string) string { if err != nil { return "" } - major := unix.Major(stat.Rdev) - minor := unix.Minor(stat.Rdev) + major := unix.Major(uint64(stat.Rdev)) + minor := unix.Minor(uint64(stat.Rdev)) // Try to get the serial from udev data udevDataPath := fmt.Sprintf("/run/udev/data/b%d:%d", major, minor) From 12fad953c173cd2614963a1faf34b3d962936978 Mon Sep 17 00:00:00 2001 From: Shrug 42 Date: Tue, 19 Jun 2018 13:12:33 -0700 Subject: [PATCH 52/62] added additional linux meminfo fields --- mem/mem.go | 35 ++++++++++++++++++++++++----------- mem/mem_linux.go | 26 ++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 11 deletions(-) diff --git a/mem/mem.go b/mem/mem.go index fd8337a..9bd2084 100644 --- a/mem/mem.go +++ b/mem/mem.go @@ -46,17 +46,30 @@ type VirtualMemoryStat struct { // https://www.centos.org/docs/5/html/5.1/Deployment_Guide/s2-proc-meminfo.html // https://www.kernel.org/doc/Documentation/filesystems/proc.txt // https://www.kernel.org/doc/Documentation/vm/overcommit-accounting - Buffers uint64 `json:"buffers"` - Cached uint64 `json:"cached"` - Writeback uint64 `json:"writeback"` - Dirty uint64 `json:"dirty"` - WritebackTmp uint64 `json:"writebacktmp"` - Shared uint64 `json:"shared"` - Slab uint64 `json:"slab"` - PageTables uint64 `json:"pagetables"` - SwapCached uint64 `json:"swapcached"` - CommitLimit uint64 `json:"commitlimit"` - CommittedAS uint64 `json:"committedas"` + Buffers uint64 `json:"buffers"` + Cached uint64 `json:"cached"` + Writeback uint64 `json:"writeback"` + Dirty uint64 `json:"dirty"` + WritebackTmp uint64 `json:"writebacktmp"` + Shared uint64 `json:"shared"` + Slab uint64 `json:"slab"` + PageTables uint64 `json:"pagetables"` + SwapCached uint64 `json:"swapcached"` + CommitLimit uint64 `json:"commitlimit"` + CommittedAS uint64 `json:"committedas"` + HighTotal uint64 `json:"hightotal"` + HighFree uint64 `json:"highfree"` + LowTotal uint64 `json:"lowtotal"` + LowFree uint64 `json:"lowfree"` + SwapTotal uint64 `json:"swaptotal"` + SwapFree uint64 `json:"swapfree"` + Mapped uint64 `json:"mapped"` + VMallocTotal uint64 `json:"vmalloctotal"` + VMallocUsed uint64 `json:"vmallocused"` + VMallocChunk uint64 `json:"vmallocchunk"` + HugePagesTotal uint64 `json:"hugePagestotal"` + HugePagesFree uint64 `json:"hugePagesfree"` + HugePageSize uint64 `json:"hugepagesize"` } type SwapMemoryStat struct { diff --git a/mem/mem_linux.go b/mem/mem_linux.go index d326fcc..fcc9a3f 100644 --- a/mem/mem_linux.go +++ b/mem/mem_linux.go @@ -69,6 +69,32 @@ func VirtualMemoryWithContext(ctx context.Context) (*VirtualMemoryStat, error) { ret.CommitLimit = t * 1024 case "Committed_AS": ret.CommittedAS = t * 1024 + case "HighTotal": + ret.HighTotal = t * 1024 + case "HighFree": + ret.HighFree = t * 1024 + case "LowTotal": + ret.LowTotal = t * 1024 + case "LowFree": + ret.LowFree = t * 1024 + case "SwapTotal": + ret.SwapTotal = t * 1024 + case "SwapFree": + ret.SwapFree = t * 1024 + case "Mapped": + ret.Mapped = t * 1024 + case "VmallocTotal": + ret.VMallocTotal = t * 1024 + case "VmallocUsed": + ret.VMallocUsed = t * 1024 + case "VmallocChunk": + ret.VMallocChunk = t * 1024 + case "HugePages_Total": + ret.HugePagesTotal = t + case "HugePages_Free": + ret.HugePagesFree = t + case "Hugepagesize": + ret.HugePageSize = t * 1024 } } if !memavail { From 6a38ce20691a51c20533cf0bcb2258162e7d6dbd Mon Sep 17 00:00:00 2001 From: Shrug 42 Date: Wed, 20 Jun 2018 10:20:54 -0700 Subject: [PATCH 53/62] fix json tags --- mem/mem.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mem/mem.go b/mem/mem.go index 9bd2084..3d71d96 100644 --- a/mem/mem.go +++ b/mem/mem.go @@ -67,8 +67,8 @@ type VirtualMemoryStat struct { VMallocTotal uint64 `json:"vmalloctotal"` VMallocUsed uint64 `json:"vmallocused"` VMallocChunk uint64 `json:"vmallocchunk"` - HugePagesTotal uint64 `json:"hugePagestotal"` - HugePagesFree uint64 `json:"hugePagesfree"` + HugePagesTotal uint64 `json:"hugepagestotal"` + HugePagesFree uint64 `json:"hugepagesfree"` HugePageSize uint64 `json:"hugepagesize"` } From 7c7dec96c6f884bfe8024cc2f69bb76b49e0e19f Mon Sep 17 00:00:00 2001 From: Michal Rostecki Date: Thu, 21 Jun 2018 16:42:49 +0200 Subject: [PATCH 54/62] cpu: Check for error object --- cpu/cpu_solaris_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cpu/cpu_solaris_test.go b/cpu/cpu_solaris_test.go index b22d385..1612753 100644 --- a/cpu/cpu_solaris_test.go +++ b/cpu/cpu_solaris_test.go @@ -102,6 +102,9 @@ func TestParseProcessorInfo(t *testing.T) { } cpus, err := parseProcessorInfo(string(content)) + if err != nil { + t.Errorf("cannot parse processor info: %s", err) + } if !reflect.DeepEqual(tc.expected, cpus) { t.Fatalf("Bad Processor Info\nExpected: %v\n Actual: %v", tc.expected, cpus) From ff2e736f72ca5cd245415063bd809a2ad59470b1 Mon Sep 17 00:00:00 2001 From: Michal Rostecki Date: Thu, 21 Jun 2018 16:48:16 +0200 Subject: [PATCH 55/62] disk: Check for error objects returned by Getfsstat --- disk/disk_darwin.go | 4 +++- disk/disk_freebsd.go | 4 +++- disk/disk_openbsd.go | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/disk/disk_darwin.go b/disk/disk_darwin.go index f9fd09c..2b1d000 100644 --- a/disk/disk_darwin.go +++ b/disk/disk_darwin.go @@ -23,7 +23,9 @@ func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, erro return ret, err } fs := make([]Statfs, count) - _, err = Getfsstat(fs, MntWait) + if _, err = Getfsstat(fs, MntWait); err != nil { + return ret, err + } for _, stat := range fs { opts := "rw" if stat.Flags&MntReadOnly != 0 { diff --git a/disk/disk_freebsd.go b/disk/disk_freebsd.go index bfb6580..2e0966a 100644 --- a/disk/disk_freebsd.go +++ b/disk/disk_freebsd.go @@ -29,7 +29,9 @@ func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, erro } fs := make([]Statfs, count) - _, err = Getfsstat(fs, MNT_WAIT) + if _, err = Getfsstat(fs, MNT_WAIT); err != nil { + return ret, err + } for _, stat := range fs { opts := "rw" diff --git a/disk/disk_openbsd.go b/disk/disk_openbsd.go index 0ac752a..6fdf386 100644 --- a/disk/disk_openbsd.go +++ b/disk/disk_openbsd.go @@ -27,7 +27,9 @@ func PartitionsWithContext(ctx context.Context, all bool) ([]PartitionStat, erro } fs := make([]Statfs, count) - _, err = Getfsstat(fs, MNT_WAIT) + if _, err = Getfsstat(fs, MNT_WAIT); err != nil { + return ret, err + } for _, stat := range fs { opts := "rw" From 91ac1d66acc846e8a4a94043b210d34d6aba6c6c Mon Sep 17 00:00:00 2001 From: Michal Rostecki Date: Thu, 21 Jun 2018 16:53:53 +0200 Subject: [PATCH 56/62] process: Check for error objects --- process/process_linux.go | 3 +++ process/process_posix.go | 6 ++++++ process/process_test.go | 3 +++ process/process_windows.go | 3 +++ 4 files changed, 15 insertions(+) diff --git a/process/process_linux.go b/process/process_linux.go index 7ec1bc5..341420b 100644 --- a/process/process_linux.go +++ b/process/process_linux.go @@ -1175,6 +1175,9 @@ func (p *Process) fillFromTIDStatWithContext(ctx context.Context, tid int32) (ui createTime := int64(ctime * 1000) rtpriority, err := strconv.ParseInt(fields[i+16], 10, 32) + if err != nil { + return 0, 0, nil, 0, 0, 0, err + } if rtpriority < 0 { rtpriority = rtpriority*-1 - 1 } else { diff --git a/process/process_posix.go b/process/process_posix.go index d058278..8ffb6b7 100644 --- a/process/process_posix.go +++ b/process/process_posix.go @@ -26,6 +26,9 @@ func getTerminalMap() (map[uint64]string, error) { defer d.Close() devnames, err := d.Readdirnames(-1) + if err != nil { + return nil, err + } for _, devname := range devnames { if strings.HasPrefix(devname, "/dev/tty") { termfiles = append(termfiles, "/dev/tty/"+devname) @@ -45,6 +48,9 @@ func getTerminalMap() (map[uint64]string, error) { if ptsnames == nil { defer ptsd.Close() ptsnames, err = ptsd.Readdirnames(-1) + if err != nil { + return nil, err + } for _, ptsname := range ptsnames { termfiles = append(termfiles, "/dev/pts/"+ptsname) } diff --git a/process/process_test.go b/process/process_test.go index d8eb483..7cdcedb 100644 --- a/process/process_test.go +++ b/process/process_test.go @@ -85,6 +85,9 @@ func Test_Process_memory_maps(t *testing.T) { checkPid := os.Getpid() ret, err := NewProcess(int32(checkPid)) + if err != nil { + t.Errorf("error %v", err) + } mmaps, err := ret.MemoryMaps(false) if err != nil { diff --git a/process/process_windows.go b/process/process_windows.go index 9304e2d..e81d67d 100644 --- a/process/process_windows.go +++ b/process/process_windows.go @@ -270,6 +270,9 @@ func (p *Process) UsernameWithContext(ctx context.Context) (string, error) { } defer token.Close() tokenUser, err := token.GetTokenUser() + if err != nil { + return "", err + } user, domain, _, err := tokenUser.User.Sid.LookupAccount("") return domain + "\\" + user, err From 0225d9ddccfd01f87d0092fe878a92b8e48e7464 Mon Sep 17 00:00:00 2001 From: Michal Rostecki Date: Thu, 21 Jun 2018 16:58:40 +0200 Subject: [PATCH 57/62] host: Check for error object --- host/host_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/host/host_test.go b/host/host_test.go index e6abb78..c211334 100644 --- a/host/host_test.go +++ b/host/host_test.go @@ -51,6 +51,9 @@ func TestBoot_time(t *testing.T) { t.Logf("first boot time: %d", v) v2, err := BootTime() + if err != nil { + t.Errorf("error %v", err) + } if v != v2 { t.Errorf("cached boot time is different") } From 9d003ad2e18b12d2b30b1955b3708bba62562d5d Mon Sep 17 00:00:00 2001 From: Michal Rostecki Date: Thu, 21 Jun 2018 17:00:15 +0200 Subject: [PATCH 58/62] net: Check for error object --- net/net_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/net_test.go b/net/net_test.go index 1559387..80f56af 100644 --- a/net/net_test.go +++ b/net/net_test.go @@ -61,6 +61,9 @@ func TestNetConnectionStatString(t *testing.T) { func TestNetIOCountersAll(t *testing.T) { v, err := IOCounters(false) + if err != nil { + t.Errorf("Could not get NetIOCounters: %v", err) + } per, err := IOCounters(true) if err != nil { t.Errorf("Could not get NetIOCounters: %v", err) From facd46a4a50590b3596d46bd991216844b4cc5ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8F=B8=E8=8A=B3=E6=BA=90?= Date: Fri, 22 Jun 2018 09:34:39 +0800 Subject: [PATCH 59/62] Implement disk.GetDiskSerialNumber for Windows via WMI. Parameter should be drive letter, so it can be used in IOCountersStat. See last part of https://msdn.microsoft.com/en-us/library/windows/desktop/aa394592(v=vs.85).aspx. --- disk/disk_windows.go | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/disk/disk_windows.go b/disk/disk_windows.go index 326bc1f..f717a89 100644 --- a/disk/disk_windows.go +++ b/disk/disk_windows.go @@ -32,6 +32,15 @@ type Win32_PerfFormattedData struct { AvgDisksecPerRead uint64 AvgDisksecPerWrite uint64 } +type Win32_DiskDrive struct { + DeviceID string + FirmwareRevision string + InterfaceType string + SerialNumber string +} +type Win32_DiskPartition struct { + DeviceID string +} const WaitMSec = 500 @@ -157,7 +166,7 @@ func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOC continue } - ret[d.Name] = IOCountersStat{ + tmpIO := IOCountersStat{ Name: d.Name, ReadCount: uint64(d.AvgDiskReadQueueLength), WriteCount: d.AvgDiskWriteQueueLength, @@ -166,6 +175,27 @@ func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOC ReadTime: d.AvgDisksecPerRead, WriteTime: d.AvgDisksecPerWrite, } + tmpIO.SerialNumber = GetDiskSerialNumber(d.Name) + ret[d.Name] = d } return ret, nil } + +// return disk serial number(not volume serial number) of given device or empty string on error. Name of device is drive letter, eg. C: +func GetDiskSerialNumber(name string) string { + return GetDiskSerialNumberWithContext(context.Background(), name) +} + +func GetDiskSerialNumberWithContext(ctx context.Context, name string) string { + var diskPart []Win32_DiskPartition + var diskDrive []Win32_DiskDrive + err := common.WMIQueryWithContext(ctx, "Associators of {Win32_LogicalDisk.DeviceID='"+name+"'} where AssocClass=Win32_LogicalDiskToPartition", &diskPart) + if err != nil || len(diskPart) <= 0 { + return "" + } + err = common.WMIQueryWithContext(ctx, "Associators of {Win32_DiskPartition.DeviceID='"+diskPart[0].DeviceID+"'} where AssocClass=Win32_DiskDriveToDiskPartition", &diskDrive) + if err != nil || len(diskDrive) <= 0 { + return "" + } + return diskDrive[0].SerialNumber +} From 98cd97162179762878938f475ca38e4a8ad1bd2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8F=B8=E8=8A=B3=E6=BA=90?= Date: Fri, 22 Jun 2018 10:03:04 +0800 Subject: [PATCH 60/62] wrong variable --- disk/disk_windows.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/disk/disk_windows.go b/disk/disk_windows.go index f717a89..6046467 100644 --- a/disk/disk_windows.go +++ b/disk/disk_windows.go @@ -176,7 +176,7 @@ func IOCountersWithContext(ctx context.Context, names ...string) (map[string]IOC WriteTime: d.AvgDisksecPerWrite, } tmpIO.SerialNumber = GetDiskSerialNumber(d.Name) - ret[d.Name] = d + ret[d.Name] = tmpIO } return ret, nil } From 1b641b25eb87669184bcd7d23ffa56b470f67ad1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8F=B8=E8=8A=B3=E6=BA=90?= Date: Fri, 22 Jun 2018 16:18:18 +0800 Subject: [PATCH 61/62] not export custom types --- disk/disk_windows.go | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/disk/disk_windows.go b/disk/disk_windows.go index 6046467..80b1127 100644 --- a/disk/disk_windows.go +++ b/disk/disk_windows.go @@ -32,13 +32,11 @@ type Win32_PerfFormattedData struct { AvgDisksecPerRead uint64 AvgDisksecPerWrite uint64 } -type Win32_DiskDrive struct { - DeviceID string - FirmwareRevision string - InterfaceType string - SerialNumber string +type win32_DiskDrive struct { + DeviceID string + SerialNumber string } -type Win32_DiskPartition struct { +type win32_DiskPartition struct { DeviceID string } @@ -187,8 +185,8 @@ func GetDiskSerialNumber(name string) string { } func GetDiskSerialNumberWithContext(ctx context.Context, name string) string { - var diskPart []Win32_DiskPartition - var diskDrive []Win32_DiskDrive + var diskPart []win32_DiskPartition + var diskDrive []win32_DiskDrive err := common.WMIQueryWithContext(ctx, "Associators of {Win32_LogicalDisk.DeviceID='"+name+"'} where AssocClass=Win32_LogicalDiskToPartition", &diskPart) if err != nil || len(diskPart) <= 0 { return "" From 9937d4109d2976145891a6a213f80686d581df9d Mon Sep 17 00:00:00 2001 From: shirou Date: Sat, 23 Jun 2018 23:38:51 +0900 Subject: [PATCH 62/62] mem[linux]: fix test. --- mem/mem_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mem/mem_test.go b/mem/mem_test.go index feeb59e..118047c 100644 --- a/mem/mem_test.go +++ b/mem/mem_test.go @@ -57,7 +57,7 @@ func TestVirtualMemoryStat_String(t *testing.T) { UsedPercent: 30.1, Free: 40, } - e := `{"total":10,"available":20,"used":30,"usedPercent":30.1,"free":40,"active":0,"inactive":0,"wired":0,"buffers":0,"cached":0,"writeback":0,"dirty":0,"writebacktmp":0,"shared":0,"slab":0,"pagetables":0,"swapcached":0,"commitlimit":0,"committedas":0}` + e := `{"total":10,"available":20,"used":30,"usedPercent":30.1,"free":40,"active":0,"inactive":0,"wired":0,"buffers":0,"cached":0,"writeback":0,"dirty":0,"writebacktmp":0,"shared":0,"slab":0,"pagetables":0,"swapcached":0,"commitlimit":0,"committedas":0,"hightotal":0,"highfree":0,"lowtotal":0,"lowfree":0,"swaptotal":0,"swapfree":0,"mapped":0,"vmalloctotal":0,"vmallocused":0,"vmallocchunk":0,"hugepagestotal":0,"hugepagesfree":0,"hugepagesize":0}` if e != fmt.Sprintf("%v", v) { t.Errorf("VirtualMemoryStat string is invalid: %v", v) }