summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGiampaolo Rodola <g.rodola@gmail.com>2023-04-20 10:47:22 +0200
committerGitHub <noreply@github.com>2023-04-20 10:47:22 +0200
commit4982b4a46da0b50951b0978e41419321d1a45ec5 (patch)
treeae07e9366184eb33483f20d9eb6ecab069abb6f6
parent3287ea328f76da7e5ff6cc148fdbd5afe33099dd (diff)
downloadpsutil-4982b4a46da0b50951b0978e41419321d1a45ec5.tar.gz
OSX big C refactoring (#2242)
-rw-r--r--MANIFEST.in10
-rw-r--r--psutil/_psutil_osx.c792
-rw-r--r--psutil/arch/osx/cpu.c71
-rw-r--r--psutil/arch/osx/cpu.h5
-rw-r--r--psutil/arch/osx/disk.c377
-rw-r--r--psutil/arch/osx/disk.h11
-rw-r--r--psutil/arch/osx/mem.c113
-rw-r--r--psutil/arch/osx/mem.h10
-rw-r--r--psutil/arch/osx/net.c101
-rw-r--r--psutil/arch/osx/net.h9
-rw-r--r--psutil/arch/osx/sensors.c102
-rw-r--r--psutil/arch/osx/sensors.h9
-rw-r--r--psutil/arch/osx/sys.c88
-rw-r--r--psutil/arch/osx/sys.h10
-rwxr-xr-xsetup.py5
15 files changed, 921 insertions, 792 deletions
diff --git a/MANIFEST.in b/MANIFEST.in
index 5d97acac..94d7708e 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -93,8 +93,18 @@ include psutil/arch/openbsd/socks.c
include psutil/arch/openbsd/socks.h
include psutil/arch/osx/cpu.c
include psutil/arch/osx/cpu.h
+include psutil/arch/osx/disk.c
+include psutil/arch/osx/disk.h
+include psutil/arch/osx/mem.c
+include psutil/arch/osx/mem.h
+include psutil/arch/osx/net.c
+include psutil/arch/osx/net.h
include psutil/arch/osx/process_info.c
include psutil/arch/osx/process_info.h
+include psutil/arch/osx/sensors.c
+include psutil/arch/osx/sensors.h
+include psutil/arch/osx/sys.c
+include psutil/arch/osx/sys.h
include psutil/arch/solaris/environ.c
include psutil/arch/solaris/environ.h
include psutil/arch/solaris/v10/ifaddrs.c
diff --git a/psutil/_psutil_osx.c b/psutil/_psutil_osx.c
index 713f3d6c..30a5312e 100644
--- a/psutil/_psutil_osx.c
+++ b/psutil/_psutil_osx.c
@@ -12,33 +12,27 @@
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
-#include <utmpx.h>
#include <sys/sysctl.h>
#include <libproc.h>
#include <sys/proc_info.h>
#include <netinet/tcp_fsm.h>
#include <arpa/inet.h>
-#include <net/if_dl.h>
#include <pwd.h>
#include <unistd.h>
#include <mach/mach.h>
#include <mach/mach_vm.h>
#include <mach/shared_region.h>
-
#include <mach-o/loader.h>
-#include <CoreFoundation/CoreFoundation.h>
-#include <IOKit/IOKitLib.h>
-#include <IOKit/storage/IOBlockStorageDriver.h>
-#include <IOKit/storage/IOMedia.h>
-#include <IOKit/IOBSD.h>
-#include <IOKit/ps/IOPowerSources.h>
-#include <IOKit/ps/IOPSKeys.h>
-
#include "_psutil_common.h"
#include "_psutil_posix.h"
-#include "arch/osx/process_info.h"
#include "arch/osx/cpu.h"
+#include "arch/osx/disk.h"
+#include "arch/osx/mem.h"
+#include "arch/osx/net.h"
+#include "arch/osx/process_info.h"
+#include "arch/osx/sensors.h"
+#include "arch/osx/sys.h"
#define PSUTIL_TV2DOUBLE(t) ((t).tv_sec + (t).tv_usec / 1000000.0)
@@ -47,28 +41,6 @@ static PyObject *ZombieProcessError;
/*
- * A wrapper around host_statistics() invoked with HOST_VM_INFO.
- */
-int
-psutil_sys_vminfo(vm_statistics_data_t *vmstat) {
- kern_return_t ret;
- mach_msg_type_number_t count = sizeof(*vmstat) / sizeof(integer_t);
- mach_port_t mport = mach_host_self();
-
- ret = host_statistics(mport, HOST_VM_INFO, (host_info_t)vmstat, &count);
- if (ret != KERN_SUCCESS) {
- PyErr_Format(
- PyExc_RuntimeError,
- "host_statistics(HOST_VM_INFO) syscall failed: %s",
- mach_error_string(ret));
- return 0;
- }
- mach_port_deallocate(mach_task_self(), mport);
- return 1;
-}
-
-
-/*
* A wrapper around task_for_pid() which sucks big time:
* - it's not documented
* - errno is set only sometimes
@@ -499,350 +471,6 @@ psutil_proc_memory_uss(PyObject *self, PyObject *args) {
/*
- * Return system virtual memory stats.
- * See:
- * https://opensource.apple.com/source/system_cmds/system_cmds-790/
- * vm_stat.tproj/vm_stat.c.auto.html
- */
-static PyObject *
-psutil_virtual_mem(PyObject *self, PyObject *args) {
- int mib[2];
- uint64_t total;
- size_t len = sizeof(total);
- vm_statistics_data_t vm;
- long pagesize = psutil_getpagesize();
- // physical mem
- mib[0] = CTL_HW;
- mib[1] = HW_MEMSIZE;
-
- // This is also available as sysctlbyname("hw.memsize").
- if (sysctl(mib, 2, &total, &len, NULL, 0)) {
- if (errno != 0)
- PyErr_SetFromErrno(PyExc_OSError);
- else
- PyErr_Format(
- PyExc_RuntimeError, "sysctl(HW_MEMSIZE) syscall failed");
- return NULL;
- }
-
- // vm
- if (!psutil_sys_vminfo(&vm))
- return NULL;
-
- return Py_BuildValue(
- "KKKKKK",
- total,
- (unsigned long long) vm.active_count * pagesize, // active
- (unsigned long long) vm.inactive_count * pagesize, // inactive
- (unsigned long long) vm.wire_count * pagesize, // wired
- (unsigned long long) vm.free_count * pagesize, // free
- (unsigned long long) vm.speculative_count * pagesize // speculative
- );
-}
-
-
-/*
- * Return stats about swap memory.
- */
-static PyObject *
-psutil_swap_mem(PyObject *self, PyObject *args) {
- int mib[2];
- size_t size;
- struct xsw_usage totals;
- vm_statistics_data_t vmstat;
- long pagesize = psutil_getpagesize();
-
- mib[0] = CTL_VM;
- mib[1] = VM_SWAPUSAGE;
- size = sizeof(totals);
- if (sysctl(mib, 2, &totals, &size, NULL, 0) == -1) {
- if (errno != 0)
- PyErr_SetFromErrno(PyExc_OSError);
- else
- PyErr_Format(
- PyExc_RuntimeError, "sysctl(VM_SWAPUSAGE) syscall failed");
- return NULL;
- }
- if (!psutil_sys_vminfo(&vmstat))
- return NULL;
-
- return Py_BuildValue(
- "LLLKK",
- totals.xsu_total,
- totals.xsu_used,
- totals.xsu_avail,
- (unsigned long long)vmstat.pageins * pagesize,
- (unsigned long long)vmstat.pageouts * pagesize);
-}
-
-
-/*
- * Return a Python list of tuple representing per-cpu times
- */
-static PyObject *
-psutil_per_cpu_times(PyObject *self, PyObject *args) {
- natural_t cpu_count;
- natural_t i;
- processor_info_array_t info_array;
- mach_msg_type_number_t info_count;
- kern_return_t error;
- processor_cpu_load_info_data_t *cpu_load_info = NULL;
- int ret;
- PyObject *py_retlist = PyList_New(0);
- PyObject *py_cputime = NULL;
-
- if (py_retlist == NULL)
- return NULL;
-
- mach_port_t host_port = mach_host_self();
- error = host_processor_info(host_port, PROCESSOR_CPU_LOAD_INFO,
- &cpu_count, &info_array, &info_count);
- if (error != KERN_SUCCESS) {
- PyErr_Format(
- PyExc_RuntimeError,
- "host_processor_info(PROCESSOR_CPU_LOAD_INFO) syscall failed: %s",
- mach_error_string(error));
- goto error;
- }
- mach_port_deallocate(mach_task_self(), host_port);
-
- cpu_load_info = (processor_cpu_load_info_data_t *) info_array;
-
- for (i = 0; i < cpu_count; i++) {
- py_cputime = Py_BuildValue(
- "(dddd)",
- (double)cpu_load_info[i].cpu_ticks[CPU_STATE_USER] / CLK_TCK,
- (double)cpu_load_info[i].cpu_ticks[CPU_STATE_NICE] / CLK_TCK,
- (double)cpu_load_info[i].cpu_ticks[CPU_STATE_SYSTEM] / CLK_TCK,
- (double)cpu_load_info[i].cpu_ticks[CPU_STATE_IDLE] / CLK_TCK
- );
- if (!py_cputime)
- goto error;
- if (PyList_Append(py_retlist, py_cputime))
- goto error;
- Py_CLEAR(py_cputime);
- }
-
- ret = vm_deallocate(mach_task_self(), (vm_address_t)info_array,
- info_count * sizeof(int));
- if (ret != KERN_SUCCESS)
- PyErr_WarnEx(PyExc_RuntimeWarning, "vm_deallocate() failed", 2);
- return py_retlist;
-
-error:
- Py_XDECREF(py_cputime);
- Py_DECREF(py_retlist);
- if (cpu_load_info != NULL) {
- ret = vm_deallocate(mach_task_self(), (vm_address_t)info_array,
- info_count * sizeof(int));
- if (ret != KERN_SUCCESS)
- PyErr_WarnEx(PyExc_RuntimeWarning, "vm_deallocate() failed", 2);
- }
- return NULL;
-}
-
-
-/*
- * Return a Python float indicating the system boot time expressed in
- * seconds since the epoch.
- */
-static PyObject *
-psutil_boot_time(PyObject *self, PyObject *args) {
- // fetch sysctl "kern.boottime"
- static int request[2] = { CTL_KERN, KERN_BOOTTIME };
- struct timeval result;
- size_t result_len = sizeof result;
- time_t boot_time = 0;
-
- if (sysctl(request, 2, &result, &result_len, NULL, 0) == -1)
- return PyErr_SetFromErrno(PyExc_OSError);
- boot_time = result.tv_sec;
- return Py_BuildValue("f", (float)boot_time);
-}
-
-
-/*
- * Return a list of tuples including device, mount point and fs type
- * for all partitions mounted on the system.
- */
-static PyObject *
-psutil_disk_partitions(PyObject *self, PyObject *args) {
- int num;
- int i;
- int len;
- uint64_t flags;
- char opts[400];
- struct statfs *fs = NULL;
- PyObject *py_dev = NULL;
- PyObject *py_mountp = NULL;
- PyObject *py_tuple = NULL;
- PyObject *py_retlist = PyList_New(0);
-
- if (py_retlist == NULL)
- return NULL;
-
- // get the number of mount points
- Py_BEGIN_ALLOW_THREADS
- num = getfsstat(NULL, 0, MNT_NOWAIT);
- Py_END_ALLOW_THREADS
- if (num == -1) {
- PyErr_SetFromErrno(PyExc_OSError);
- goto error;
- }
-
- len = sizeof(*fs) * num;
- fs = malloc(len);
- if (fs == NULL) {
- PyErr_NoMemory();
- goto error;
- }
-
- Py_BEGIN_ALLOW_THREADS
- num = getfsstat(fs, len, MNT_NOWAIT);
- Py_END_ALLOW_THREADS
- if (num == -1) {
- PyErr_SetFromErrno(PyExc_OSError);
- goto error;
- }
-
- for (i = 0; i < num; i++) {
- opts[0] = 0;
- flags = fs[i].f_flags;
-
- // see sys/mount.h
- if (flags & MNT_RDONLY)
- strlcat(opts, "ro", sizeof(opts));
- else
- strlcat(opts, "rw", sizeof(opts));
- if (flags & MNT_SYNCHRONOUS)
- strlcat(opts, ",sync", sizeof(opts));
- if (flags & MNT_NOEXEC)
- strlcat(opts, ",noexec", sizeof(opts));
- if (flags & MNT_NOSUID)
- strlcat(opts, ",nosuid", sizeof(opts));
- if (flags & MNT_UNION)
- strlcat(opts, ",union", sizeof(opts));
- if (flags & MNT_ASYNC)
- strlcat(opts, ",async", sizeof(opts));
- if (flags & MNT_EXPORTED)
- strlcat(opts, ",exported", sizeof(opts));
- if (flags & MNT_QUARANTINE)
- strlcat(opts, ",quarantine", sizeof(opts));
- if (flags & MNT_LOCAL)
- strlcat(opts, ",local", sizeof(opts));
- if (flags & MNT_QUOTA)
- strlcat(opts, ",quota", sizeof(opts));
- if (flags & MNT_ROOTFS)
- strlcat(opts, ",rootfs", sizeof(opts));
- if (flags & MNT_DOVOLFS)
- strlcat(opts, ",dovolfs", sizeof(opts));
- if (flags & MNT_DONTBROWSE)
- strlcat(opts, ",dontbrowse", sizeof(opts));
- if (flags & MNT_IGNORE_OWNERSHIP)
- strlcat(opts, ",ignore-ownership", sizeof(opts));
- if (flags & MNT_AUTOMOUNTED)
- strlcat(opts, ",automounted", sizeof(opts));
- if (flags & MNT_JOURNALED)
- strlcat(opts, ",journaled", sizeof(opts));
- if (flags & MNT_NOUSERXATTR)
- strlcat(opts, ",nouserxattr", sizeof(opts));
- if (flags & MNT_DEFWRITE)
- strlcat(opts, ",defwrite", sizeof(opts));
- if (flags & MNT_MULTILABEL)
- strlcat(opts, ",multilabel", sizeof(opts));
- if (flags & MNT_NOATIME)
- strlcat(opts, ",noatime", sizeof(opts));
- if (flags & MNT_UPDATE)
- strlcat(opts, ",update", sizeof(opts));
- if (flags & MNT_RELOAD)
- strlcat(opts, ",reload", sizeof(opts));
- if (flags & MNT_FORCE)
- strlcat(opts, ",force", sizeof(opts));
- if (flags & MNT_CMDFLAGS)
- strlcat(opts, ",cmdflags", sizeof(opts));
-
- py_dev = PyUnicode_DecodeFSDefault(fs[i].f_mntfromname);
- if (! py_dev)
- goto error;
- py_mountp = PyUnicode_DecodeFSDefault(fs[i].f_mntonname);
- if (! py_mountp)
- goto error;
- py_tuple = Py_BuildValue(
- "(OOss)",
- py_dev, // device
- py_mountp, // mount point
- fs[i].f_fstypename, // fs type
- opts); // options
- if (!py_tuple)
- goto error;
- if (PyList_Append(py_retlist, py_tuple))
- goto error;
- Py_CLEAR(py_dev);
- Py_CLEAR(py_mountp);
- Py_CLEAR(py_tuple);
- }
-
- free(fs);
- return py_retlist;
-
-error:
- Py_XDECREF(py_dev);
- Py_XDECREF(py_mountp);
- Py_XDECREF(py_tuple);
- Py_DECREF(py_retlist);
- if (fs != NULL)
- free(fs);
- return NULL;
-}
-
-
-static PyObject *
-psutil_disk_usage_used(PyObject *self, PyObject *args) {
- PyObject *py_default_value;
- PyObject *py_mount_point_bytes = NULL;
- char* mount_point;
-
-#if PY_MAJOR_VERSION >= 3
- if (!PyArg_ParseTuple(args, "O&O", PyUnicode_FSConverter, &py_mount_point_bytes, &py_default_value)) {
- return NULL;
- }
- mount_point = PyBytes_AsString(py_mount_point_bytes);
- if (NULL == mount_point) {
- Py_XDECREF(py_mount_point_bytes);
- return NULL;
- }
-#else
- if (!PyArg_ParseTuple(args, "sO", &mount_point, &py_default_value)) {
- return NULL;
- }
-#endif
-
-#ifdef ATTR_VOL_SPACEUSED
- /* Call getattrlist(ATTR_VOL_SPACEUSED) to get used space info. */
- int ret;
- struct {
- uint32_t size;
- uint64_t spaceused;
- } __attribute__((aligned(4), packed)) attrbuf = {0};
- struct attrlist attrs = {0};
-
- attrs.bitmapcount = ATTR_BIT_MAP_COUNT;
- attrs.volattr = ATTR_VOL_INFO | ATTR_VOL_SPACEUSED;
- Py_BEGIN_ALLOW_THREADS
- ret = getattrlist(mount_point, &attrs, &attrbuf, sizeof(attrbuf), 0);
- Py_END_ALLOW_THREADS
- if (ret == 0) {
- Py_XDECREF(py_mount_point_bytes);
- return PyLong_FromUnsignedLongLong(attrbuf.spaceused);
- }
- psutil_debug("getattrlist(ATTR_VOL_SPACEUSED) failed, fall-back to default value");
-#endif
- Py_XDECREF(py_mount_point_bytes);
- Py_INCREF(py_default_value);
- return py_default_value;
-}
-
-/*
* Return process threads
*/
static PyObject *
@@ -1254,414 +882,6 @@ psutil_proc_num_fds(PyObject *self, PyObject *args) {
/*
- * Return a Python list of named tuples with overall network I/O information
- */
-static PyObject *
-psutil_net_io_counters(PyObject *self, PyObject *args) {
- char *buf = NULL, *lim, *next;
- struct if_msghdr *ifm;
- int mib[6];
- mib[0] = CTL_NET; // networking subsystem
- mib[1] = PF_ROUTE; // type of information
- mib[2] = 0; // protocol (IPPROTO_xxx)
- mib[3] = 0; // address family
- mib[4] = NET_RT_IFLIST2; // operation
- mib[5] = 0;
- size_t len;
- PyObject *py_ifc_info = NULL;
- PyObject *py_retdict = PyDict_New();
-
- if (py_retdict == NULL)
- return NULL;
-
- if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
- PyErr_SetFromErrno(PyExc_OSError);
- goto error;
- }
-
- buf = malloc(len);
- if (buf == NULL) {
- PyErr_NoMemory();
- goto error;
- }
-
- if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
- PyErr_SetFromErrno(PyExc_OSError);
- goto error;
- }
-
- lim = buf + len;
-
- for (next = buf; next < lim; ) {
- ifm = (struct if_msghdr *)next;
- next += ifm->ifm_msglen;
-
- if (ifm->ifm_type == RTM_IFINFO2) {
- py_ifc_info = NULL;
- struct if_msghdr2 *if2m = (struct if_msghdr2 *)ifm;
- struct sockaddr_dl *sdl = (struct sockaddr_dl *)(if2m + 1);
- char ifc_name[32];
-
- strncpy(ifc_name, sdl->sdl_data, sdl->sdl_nlen);
- ifc_name[sdl->sdl_nlen] = 0;
-
- py_ifc_info = Py_BuildValue(
- "(KKKKKKKi)",
- if2m->ifm_data.ifi_obytes,
- if2m->ifm_data.ifi_ibytes,
- if2m->ifm_data.ifi_opackets,
- if2m->ifm_data.ifi_ipackets,
- if2m->ifm_data.ifi_ierrors,
- if2m->ifm_data.ifi_oerrors,
- if2m->ifm_data.ifi_iqdrops,
- 0); // dropout not supported
-
- if (!py_ifc_info)
- goto error;
- if (PyDict_SetItemString(py_retdict, ifc_name, py_ifc_info))
- goto error;
- Py_CLEAR(py_ifc_info);
- }
- else {
- continue;
- }
- }
-
- free(buf);
- return py_retdict;
-
-error:
- Py_XDECREF(py_ifc_info);
- Py_DECREF(py_retdict);
- if (buf != NULL)
- free(buf);
- return NULL;
-}
-
-
-/*
- * Return a Python dict of tuples for disk I/O information
- */
-static PyObject *
-psutil_disk_io_counters(PyObject *self, PyObject *args) {
- CFDictionaryRef parent_dict;
- CFDictionaryRef props_dict;
- CFDictionaryRef stats_dict;
- io_registry_entry_t parent;
- io_registry_entry_t disk;
- io_iterator_t disk_list;
- PyObject *py_disk_info = NULL;
- PyObject *py_retdict = PyDict_New();
-
- if (py_retdict == NULL)
- return NULL;
-
- // Get list of disks
- if (IOServiceGetMatchingServices(kIOMasterPortDefault,
- IOServiceMatching(kIOMediaClass),
- &disk_list) != kIOReturnSuccess) {
- PyErr_SetString(
- PyExc_RuntimeError, "unable to get the list of disks.");
- goto error;
- }
-
- // Iterate over disks
- while ((disk = IOIteratorNext(disk_list)) != 0) {
- py_disk_info = NULL;
- parent_dict = NULL;
- props_dict = NULL;
- stats_dict = NULL;
-
- if (IORegistryEntryGetParentEntry(disk, kIOServicePlane, &parent)
- != kIOReturnSuccess) {
- PyErr_SetString(PyExc_RuntimeError,
- "unable to get the disk's parent.");
- IOObjectRelease(disk);
- goto error;
- }
-
- if (IOObjectConformsTo(parent, "IOBlockStorageDriver")) {
- if (IORegistryEntryCreateCFProperties(
- disk,
- (CFMutableDictionaryRef *) &parent_dict,
- kCFAllocatorDefault,
- kNilOptions
- ) != kIOReturnSuccess)
- {
- PyErr_SetString(PyExc_RuntimeError,
- "unable to get the parent's properties.");
- IOObjectRelease(disk);
- IOObjectRelease(parent);
- goto error;
- }
-
- if (IORegistryEntryCreateCFProperties(
- parent,
- (CFMutableDictionaryRef *) &props_dict,
- kCFAllocatorDefault,
- kNilOptions
- ) != kIOReturnSuccess)
- {
- PyErr_SetString(PyExc_RuntimeError,
- "unable to get the disk properties.");
- CFRelease(props_dict);
- IOObjectRelease(disk);
- IOObjectRelease(parent);
- goto error;
- }
-
- const int kMaxDiskNameSize = 64;
- CFStringRef disk_name_ref = (CFStringRef)CFDictionaryGetValue(
- parent_dict, CFSTR(kIOBSDNameKey));
- char disk_name[kMaxDiskNameSize];
-
- CFStringGetCString(disk_name_ref,
- disk_name,
- kMaxDiskNameSize,
- CFStringGetSystemEncoding());
-
- stats_dict = (CFDictionaryRef)CFDictionaryGetValue(
- props_dict, CFSTR(kIOBlockStorageDriverStatisticsKey));
-
- if (stats_dict == NULL) {
- PyErr_SetString(PyExc_RuntimeError,
- "Unable to get disk stats.");
- goto error;
- }
-
- CFNumberRef number;
- int64_t reads = 0;
- int64_t writes = 0;
- int64_t read_bytes = 0;
- int64_t write_bytes = 0;
- int64_t read_time = 0;
- int64_t write_time = 0;
-
- // Get disk reads/writes
- if ((number = (CFNumberRef)CFDictionaryGetValue(
- stats_dict,
- CFSTR(kIOBlockStorageDriverStatisticsReadsKey))))
- {
- CFNumberGetValue(number, kCFNumberSInt64Type, &reads);
- }
- if ((number = (CFNumberRef)CFDictionaryGetValue(
- stats_dict,
- CFSTR(kIOBlockStorageDriverStatisticsWritesKey))))
- {
- CFNumberGetValue(number, kCFNumberSInt64Type, &writes);
- }
-
- // Get disk bytes read/written
- if ((number = (CFNumberRef)CFDictionaryGetValue(
- stats_dict,
- CFSTR(kIOBlockStorageDriverStatisticsBytesReadKey))))
- {
- CFNumberGetValue(number, kCFNumberSInt64Type, &read_bytes);
- }
- if ((number = (CFNumberRef)CFDictionaryGetValue(
- stats_dict,
- CFSTR(kIOBlockStorageDriverStatisticsBytesWrittenKey))))
- {
- CFNumberGetValue(number, kCFNumberSInt64Type, &write_bytes);
- }
-
- // Get disk time spent reading/writing (nanoseconds)
- if ((number = (CFNumberRef)CFDictionaryGetValue(
- stats_dict,
- CFSTR(kIOBlockStorageDriverStatisticsTotalReadTimeKey))))
- {
- CFNumberGetValue(number, kCFNumberSInt64Type, &read_time);
- }
- if ((number = (CFNumberRef)CFDictionaryGetValue(
- stats_dict,
- CFSTR(kIOBlockStorageDriverStatisticsTotalWriteTimeKey))))
- {
- CFNumberGetValue(number, kCFNumberSInt64Type, &write_time);
- }
-
- // Read/Write time on macOS comes back in nanoseconds and in psutil
- // we've standardized on milliseconds so do the conversion.
- py_disk_info = Py_BuildValue(
- "(KKKKKK)",
- reads,
- writes,
- read_bytes,
- write_bytes,
- read_time / 1000 / 1000,
- write_time / 1000 / 1000);
- if (!py_disk_info)
- goto error;
- if (PyDict_SetItemString(py_retdict, disk_name, py_disk_info))
- goto error;
- Py_CLEAR(py_disk_info);
-
- CFRelease(parent_dict);
- IOObjectRelease(parent);
- CFRelease(props_dict);
- IOObjectRelease(disk);
- }
- }
-
- IOObjectRelease (disk_list);
-
- return py_retdict;
-
-error:
- Py_XDECREF(py_disk_info);
- Py_DECREF(py_retdict);
- return NULL;
-}
-
-
-/*
- * Return currently connected users as a list of tuples.
- */
-static PyObject *
-psutil_users(PyObject *self, PyObject *args) {
- struct utmpx *utx;
- PyObject *py_username = NULL;
- PyObject *py_tty = NULL;
- PyObject *py_hostname = NULL;
- PyObject *py_tuple = NULL;
- PyObject *py_retlist = PyList_New(0);
-
- if (py_retlist == NULL)
- return NULL;
- while ((utx = getutxent()) != NULL) {
- if (utx->ut_type != USER_PROCESS)
- continue;
- py_username = PyUnicode_DecodeFSDefault(utx->ut_user);
- if (! py_username)
- goto error;
- py_tty = PyUnicode_DecodeFSDefault(utx->ut_line);
- if (! py_tty)
- goto error;
- py_hostname = PyUnicode_DecodeFSDefault(utx->ut_host);
- if (! py_hostname)
- goto error;
- py_tuple = Py_BuildValue(
- "(OOOdi)",
- py_username, // username
- py_tty, // tty
- py_hostname, // hostname
- (double)utx->ut_tv.tv_sec, // start time
- utx->ut_pid // process id
- );
- if (!py_tuple) {
- endutxent();
- goto error;
- }
- if (PyList_Append(py_retlist, py_tuple)) {
- endutxent();
- goto error;
- }
- Py_CLEAR(py_username);
- Py_CLEAR(py_tty);
- Py_CLEAR(py_hostname);
- Py_CLEAR(py_tuple);
- }
-
- endutxent();
- return py_retlist;
-
-error:
- Py_XDECREF(py_username);
- Py_XDECREF(py_tty);
- Py_XDECREF(py_hostname);
- Py_XDECREF(py_tuple);
- Py_DECREF(py_retlist);
- return NULL;
-}
-
-
-/*
- * Return battery information.
- */
-static PyObject *
-psutil_sensors_battery(PyObject *self, PyObject *args) {
- PyObject *py_tuple = NULL;
- CFTypeRef power_info = NULL;
- CFArrayRef power_sources_list = NULL;
- CFDictionaryRef power_sources_information = NULL;
- CFNumberRef capacity_ref = NULL;
- CFNumberRef time_to_empty_ref = NULL;
- CFStringRef ps_state_ref = NULL;
- uint32_t capacity; /* units are percent */
- int time_to_empty; /* units are minutes */
- int is_power_plugged;
-
- power_info = IOPSCopyPowerSourcesInfo();
-
- if (!power_info) {
- PyErr_SetString(PyExc_RuntimeError,
- "IOPSCopyPowerSourcesInfo() syscall failed");
- goto error;
- }
-
- power_sources_list = IOPSCopyPowerSourcesList(power_info);
- if (!power_sources_list) {
- PyErr_SetString(PyExc_RuntimeError,
- "IOPSCopyPowerSourcesList() syscall failed");
- goto error;
- }
-
- /* Should only get one source. But in practice, check for > 0 sources */
- if (!CFArrayGetCount(power_sources_list)) {
- PyErr_SetString(PyExc_NotImplementedError, "no battery");
- goto error;
- }
-
- power_sources_information = IOPSGetPowerSourceDescription(
- power_info, CFArrayGetValueAtIndex(power_sources_list, 0));
-
- capacity_ref = (CFNumberRef) CFDictionaryGetValue(
- power_sources_information, CFSTR(kIOPSCurrentCapacityKey));
- if (!CFNumberGetValue(capacity_ref, kCFNumberSInt32Type, &capacity)) {
- PyErr_SetString(PyExc_RuntimeError,
- "No battery capacity infomration in power sources info");
- goto error;
- }
-
- ps_state_ref = (CFStringRef) CFDictionaryGetValue(
- power_sources_information, CFSTR(kIOPSPowerSourceStateKey));
- is_power_plugged = CFStringCompare(
- ps_state_ref, CFSTR(kIOPSACPowerValue), 0)
- == kCFCompareEqualTo;
-
- time_to_empty_ref = (CFNumberRef) CFDictionaryGetValue(
- power_sources_information, CFSTR(kIOPSTimeToEmptyKey));
- if (!CFNumberGetValue(time_to_empty_ref,
- kCFNumberIntType, &time_to_empty)) {
- /* This value is recommended for non-Apple power sources, so it's not
- * an error if it doesn't exist. We'll return -1 for "unknown" */
- /* A value of -1 indicates "Still Calculating the Time" also for
- * apple power source */
- time_to_empty = -1;
- }
-
- py_tuple = Py_BuildValue("Iii",
- capacity, time_to_empty, is_power_plugged);
- if (!py_tuple) {
- goto error;
- }
-
- CFRelease(power_info);
- CFRelease(power_sources_list);
- /* Caller should NOT release power_sources_information */
-
- return py_tuple;
-
-error:
- if (power_info)
- CFRelease(power_info);
- if (power_sources_list)
- CFRelease(power_sources_list);
- Py_XDECREF(py_tuple);
- return NULL;
-}
-
-
-/*
* define the psutil C module methods and initialize the module.
*/
static PyMethodDef mod_methods[] = {
diff --git a/psutil/arch/osx/cpu.c b/psutil/arch/osx/cpu.c
index 6e564718..a1ba1142 100644
--- a/psutil/arch/osx/cpu.c
+++ b/psutil/arch/osx/cpu.c
@@ -19,18 +19,18 @@ For reference, here's the git history with original implementations:
*/
#include <Python.h>
-#include <sys/sysctl.h>
-#include <sys/vmmeter.h>
-
#include <mach/mach_error.h>
#include <mach/mach_host.h>
#include <mach/mach_port.h>
+#include <mach/mach_vm.h>
+#include <sys/sysctl.h>
+#include <sys/vmmeter.h>
+#include <mach/mach.h>
#include "../../_psutil_common.h"
#include "../../_psutil_posix.h"
-
PyObject *
psutil_cpu_count_logical(PyObject *self, PyObject *args) {
int num;
@@ -138,3 +138,66 @@ psutil_cpu_freq(PyObject *self, PyObject *args) {
min / 1000 / 1000,
max / 1000 / 1000);
}
+
+
+PyObject *
+psutil_per_cpu_times(PyObject *self, PyObject *args) {
+ natural_t cpu_count;
+ natural_t i;
+ processor_info_array_t info_array;
+ mach_msg_type_number_t info_count;
+ kern_return_t error;
+ processor_cpu_load_info_data_t *cpu_load_info = NULL;
+ int ret;
+ PyObject *py_retlist = PyList_New(0);
+ PyObject *py_cputime = NULL;
+
+ if (py_retlist == NULL)
+ return NULL;
+
+ mach_port_t host_port = mach_host_self();
+ error = host_processor_info(host_port, PROCESSOR_CPU_LOAD_INFO,
+ &cpu_count, &info_array, &info_count);
+ if (error != KERN_SUCCESS) {
+ PyErr_Format(
+ PyExc_RuntimeError,
+ "host_processor_info(PROCESSOR_CPU_LOAD_INFO) syscall failed: %s",
+ mach_error_string(error));
+ goto error;
+ }
+ mach_port_deallocate(mach_task_self(), host_port);
+
+ cpu_load_info = (processor_cpu_load_info_data_t *) info_array;
+
+ for (i = 0; i < cpu_count; i++) {
+ py_cputime = Py_BuildValue(
+ "(dddd)",
+ (double)cpu_load_info[i].cpu_ticks[CPU_STATE_USER] / CLK_TCK,
+ (double)cpu_load_info[i].cpu_ticks[CPU_STATE_NICE] / CLK_TCK,
+ (double)cpu_load_info[i].cpu_ticks[CPU_STATE_SYSTEM] / CLK_TCK,
+ (double)cpu_load_info[i].cpu_ticks[CPU_STATE_IDLE] / CLK_TCK
+ );
+ if (!py_cputime)
+ goto error;
+ if (PyList_Append(py_retlist, py_cputime))
+ goto error;
+ Py_CLEAR(py_cputime);
+ }
+
+ ret = vm_deallocate(mach_task_self(), (vm_address_t)info_array,
+ info_count * sizeof(int));
+ if (ret != KERN_SUCCESS)
+ PyErr_WarnEx(PyExc_RuntimeWarning, "vm_deallocate() failed", 2);
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_cputime);
+ Py_DECREF(py_retlist);
+ if (cpu_load_info != NULL) {
+ ret = vm_deallocate(mach_task_self(), (vm_address_t)info_array,
+ info_count * sizeof(int));
+ if (ret != KERN_SUCCESS)
+ PyErr_WarnEx(PyExc_RuntimeWarning, "vm_deallocate() failed", 2);
+ }
+ return NULL;
+}
diff --git a/psutil/arch/osx/cpu.h b/psutil/arch/osx/cpu.h
index aac0f809..6cf92f82 100644
--- a/psutil/arch/osx/cpu.h
+++ b/psutil/arch/osx/cpu.h
@@ -6,8 +6,9 @@
#include <Python.h>
-PyObject *psutil_cpu_count_logical(PyObject *self, PyObject *args);
PyObject *psutil_cpu_count_cores(PyObject *self, PyObject *args);
-PyObject *psutil_cpu_times(PyObject *self, PyObject *args);
+PyObject *psutil_cpu_count_logical(PyObject *self, PyObject *args);
PyObject *psutil_cpu_freq(PyObject *self, PyObject *args);
PyObject *psutil_cpu_stats(PyObject *self, PyObject *args);
+PyObject *psutil_cpu_times(PyObject *self, PyObject *args);
+PyObject *psutil_per_cpu_times(PyObject *self, PyObject *args);
diff --git a/psutil/arch/osx/disk.c b/psutil/arch/osx/disk.c
new file mode 100644
index 00000000..961fc42a
--- /dev/null
+++ b/psutil/arch/osx/disk.c
@@ -0,0 +1,377 @@
+/*
+ * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// Disk related functions. Original code was refactored and moved
+// from psutil/_psutil_osx.c in 2023. This is the GIT blame before the move:
+// https://github.com/giampaolo/psutil/blame/efd7ed3/psutil/_psutil_osx.c
+
+#include <Python.h>
+#include <sys/param.h>
+#include <sys/ucred.h>
+#include <sys/mount.h>
+#include <CoreFoundation/CoreFoundation.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/storage/IOBlockStorageDriver.h>
+#include <IOKit/storage/IOMedia.h>
+#include <IOKit/IOBSD.h>
+
+#include "../../_psutil_common.h"
+
+
+/*
+ * Return a list of tuples including device, mount point and fs type
+ * for all partitions mounted on the system.
+ */
+PyObject *
+psutil_disk_partitions(PyObject *self, PyObject *args) {
+ int num;
+ int i;
+ int len;
+ uint64_t flags;
+ char opts[400];
+ struct statfs *fs = NULL;
+ PyObject *py_dev = NULL;
+ PyObject *py_mountp = NULL;
+ PyObject *py_tuple = NULL;
+ PyObject *py_retlist = PyList_New(0);
+
+ if (py_retlist == NULL)
+ return NULL;
+
+ // get the number of mount points
+ Py_BEGIN_ALLOW_THREADS
+ num = getfsstat(NULL, 0, MNT_NOWAIT);
+ Py_END_ALLOW_THREADS
+ if (num == -1) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+
+ len = sizeof(*fs) * num;
+ fs = malloc(len);
+ if (fs == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+
+ Py_BEGIN_ALLOW_THREADS
+ num = getfsstat(fs, len, MNT_NOWAIT);
+ Py_END_ALLOW_THREADS
+ if (num == -1) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+
+ for (i = 0; i < num; i++) {
+ opts[0] = 0;
+ flags = fs[i].f_flags;
+
+ // see sys/mount.h
+ if (flags & MNT_RDONLY)
+ strlcat(opts, "ro", sizeof(opts));
+ else
+ strlcat(opts, "rw", sizeof(opts));
+ if (flags & MNT_SYNCHRONOUS)
+ strlcat(opts, ",sync", sizeof(opts));
+ if (flags & MNT_NOEXEC)
+ strlcat(opts, ",noexec", sizeof(opts));
+ if (flags & MNT_NOSUID)
+ strlcat(opts, ",nosuid", sizeof(opts));
+ if (flags & MNT_UNION)
+ strlcat(opts, ",union", sizeof(opts));
+ if (flags & MNT_ASYNC)
+ strlcat(opts, ",async", sizeof(opts));
+ if (flags & MNT_EXPORTED)
+ strlcat(opts, ",exported", sizeof(opts));
+ if (flags & MNT_QUARANTINE)
+ strlcat(opts, ",quarantine", sizeof(opts));
+ if (flags & MNT_LOCAL)
+ strlcat(opts, ",local", sizeof(opts));
+ if (flags & MNT_QUOTA)
+ strlcat(opts, ",quota", sizeof(opts));
+ if (flags & MNT_ROOTFS)
+ strlcat(opts, ",rootfs", sizeof(opts));
+ if (flags & MNT_DOVOLFS)
+ strlcat(opts, ",dovolfs", sizeof(opts));
+ if (flags & MNT_DONTBROWSE)
+ strlcat(opts, ",dontbrowse", sizeof(opts));
+ if (flags & MNT_IGNORE_OWNERSHIP)
+ strlcat(opts, ",ignore-ownership", sizeof(opts));
+ if (flags & MNT_AUTOMOUNTED)
+ strlcat(opts, ",automounted", sizeof(opts));
+ if (flags & MNT_JOURNALED)
+ strlcat(opts, ",journaled", sizeof(opts));
+ if (flags & MNT_NOUSERXATTR)
+ strlcat(opts, ",nouserxattr", sizeof(opts));
+ if (flags & MNT_DEFWRITE)
+ strlcat(opts, ",defwrite", sizeof(opts));
+ if (flags & MNT_MULTILABEL)
+ strlcat(opts, ",multilabel", sizeof(opts));
+ if (flags & MNT_NOATIME)
+ strlcat(opts, ",noatime", sizeof(opts));
+ if (flags & MNT_UPDATE)
+ strlcat(opts, ",update", sizeof(opts));
+ if (flags & MNT_RELOAD)
+ strlcat(opts, ",reload", sizeof(opts));
+ if (flags & MNT_FORCE)
+ strlcat(opts, ",force", sizeof(opts));
+ if (flags & MNT_CMDFLAGS)
+ strlcat(opts, ",cmdflags", sizeof(opts));
+
+ py_dev = PyUnicode_DecodeFSDefault(fs[i].f_mntfromname);
+ if (! py_dev)
+ goto error;
+ py_mountp = PyUnicode_DecodeFSDefault(fs[i].f_mntonname);
+ if (! py_mountp)
+ goto error;
+ py_tuple = Py_BuildValue(
+ "(OOss)",
+ py_dev, // device
+ py_mountp, // mount point
+ fs[i].f_fstypename, // fs type
+ opts); // options
+ if (!py_tuple)
+ goto error;
+ if (PyList_Append(py_retlist, py_tuple))
+ goto error;
+ Py_CLEAR(py_dev);
+ Py_CLEAR(py_mountp);
+ Py_CLEAR(py_tuple);
+ }
+
+ free(fs);
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_dev);
+ Py_XDECREF(py_mountp);
+ Py_XDECREF(py_tuple);
+ Py_DECREF(py_retlist);
+ if (fs != NULL)
+ free(fs);
+ return NULL;
+}
+
+
+PyObject *
+psutil_disk_usage_used(PyObject *self, PyObject *args) {
+ PyObject *py_default_value;
+ PyObject *py_mount_point_bytes = NULL;
+ char* mount_point;
+
+#if PY_MAJOR_VERSION >= 3
+ if (!PyArg_ParseTuple(args, "O&O", PyUnicode_FSConverter, &py_mount_point_bytes, &py_default_value)) {
+ return NULL;
+ }
+ mount_point = PyBytes_AsString(py_mount_point_bytes);
+ if (NULL == mount_point) {
+ Py_XDECREF(py_mount_point_bytes);
+ return NULL;
+ }
+#else
+ if (!PyArg_ParseTuple(args, "sO", &mount_point, &py_default_value)) {
+ return NULL;
+ }
+#endif
+
+#ifdef ATTR_VOL_SPACEUSED
+ /* Call getattrlist(ATTR_VOL_SPACEUSED) to get used space info. */
+ int ret;
+ struct {
+ uint32_t size;
+ uint64_t spaceused;
+ } __attribute__((aligned(4), packed)) attrbuf = {0};
+ struct attrlist attrs = {0};
+
+ attrs.bitmapcount = ATTR_BIT_MAP_COUNT;
+ attrs.volattr = ATTR_VOL_INFO | ATTR_VOL_SPACEUSED;
+ Py_BEGIN_ALLOW_THREADS
+ ret = getattrlist(mount_point, &attrs, &attrbuf, sizeof(attrbuf), 0);
+ Py_END_ALLOW_THREADS
+ if (ret == 0) {
+ Py_XDECREF(py_mount_point_bytes);
+ return PyLong_FromUnsignedLongLong(attrbuf.spaceused);
+ }
+ psutil_debug("getattrlist(ATTR_VOL_SPACEUSED) failed, fall-back to default value");
+#endif
+ Py_XDECREF(py_mount_point_bytes);
+ Py_INCREF(py_default_value);
+ return py_default_value;
+}
+
+
+/*
+ * Return a Python dict of tuples for disk I/O information
+ */
+PyObject *
+psutil_disk_io_counters(PyObject *self, PyObject *args) {
+ CFDictionaryRef parent_dict;
+ CFDictionaryRef props_dict;
+ CFDictionaryRef stats_dict;
+ io_registry_entry_t parent;
+ io_registry_entry_t disk;
+ io_iterator_t disk_list;
+ PyObject *py_disk_info = NULL;
+ PyObject *py_retdict = PyDict_New();
+
+ if (py_retdict == NULL)
+ return NULL;
+
+ // Get list of disks
+ if (IOServiceGetMatchingServices(kIOMasterPortDefault,
+ IOServiceMatching(kIOMediaClass),
+ &disk_list) != kIOReturnSuccess) {
+ PyErr_SetString(
+ PyExc_RuntimeError, "unable to get the list of disks.");
+ goto error;
+ }
+
+ // Iterate over disks
+ while ((disk = IOIteratorNext(disk_list)) != 0) {
+ py_disk_info = NULL;
+ parent_dict = NULL;
+ props_dict = NULL;
+ stats_dict = NULL;
+
+ if (IORegistryEntryGetParentEntry(disk, kIOServicePlane, &parent)
+ != kIOReturnSuccess) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "unable to get the disk's parent.");
+ IOObjectRelease(disk);
+ goto error;
+ }
+
+ if (IOObjectConformsTo(parent, "IOBlockStorageDriver")) {
+ if (IORegistryEntryCreateCFProperties(
+ disk,
+ (CFMutableDictionaryRef *) &parent_dict,
+ kCFAllocatorDefault,
+ kNilOptions
+ ) != kIOReturnSuccess)
+ {
+ PyErr_SetString(PyExc_RuntimeError,
+ "unable to get the parent's properties.");
+ IOObjectRelease(disk);
+ IOObjectRelease(parent);
+ goto error;
+ }
+
+ if (IORegistryEntryCreateCFProperties(
+ parent,
+ (CFMutableDictionaryRef *) &props_dict,
+ kCFAllocatorDefault,
+ kNilOptions
+ ) != kIOReturnSuccess)
+ {
+ PyErr_SetString(PyExc_RuntimeError,
+ "unable to get the disk properties.");
+ CFRelease(props_dict);
+ IOObjectRelease(disk);
+ IOObjectRelease(parent);
+ goto error;
+ }
+
+ const int kMaxDiskNameSize = 64;
+ CFStringRef disk_name_ref = (CFStringRef)CFDictionaryGetValue(
+ parent_dict, CFSTR(kIOBSDNameKey));
+ char disk_name[kMaxDiskNameSize];
+
+ CFStringGetCString(disk_name_ref,
+ disk_name,
+ kMaxDiskNameSize,
+ CFStringGetSystemEncoding());
+
+ stats_dict = (CFDictionaryRef)CFDictionaryGetValue(
+ props_dict, CFSTR(kIOBlockStorageDriverStatisticsKey));
+
+ if (stats_dict == NULL) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "Unable to get disk stats.");
+ goto error;
+ }
+
+ CFNumberRef number;
+ int64_t reads = 0;
+ int64_t writes = 0;
+ int64_t read_bytes = 0;
+ int64_t write_bytes = 0;
+ int64_t read_time = 0;
+ int64_t write_time = 0;
+
+ // Get disk reads/writes
+ if ((number = (CFNumberRef)CFDictionaryGetValue(
+ stats_dict,
+ CFSTR(kIOBlockStorageDriverStatisticsReadsKey))))
+ {
+ CFNumberGetValue(number, kCFNumberSInt64Type, &reads);
+ }
+ if ((number = (CFNumberRef)CFDictionaryGetValue(
+ stats_dict,
+ CFSTR(kIOBlockStorageDriverStatisticsWritesKey))))
+ {
+ CFNumberGetValue(number, kCFNumberSInt64Type, &writes);
+ }
+
+ // Get disk bytes read/written
+ if ((number = (CFNumberRef)CFDictionaryGetValue(
+ stats_dict,
+ CFSTR(kIOBlockStorageDriverStatisticsBytesReadKey))))
+ {
+ CFNumberGetValue(number, kCFNumberSInt64Type, &read_bytes);
+ }
+ if ((number = (CFNumberRef)CFDictionaryGetValue(
+ stats_dict,
+ CFSTR(kIOBlockStorageDriverStatisticsBytesWrittenKey))))
+ {
+ CFNumberGetValue(number, kCFNumberSInt64Type, &write_bytes);
+ }
+
+ // Get disk time spent reading/writing (nanoseconds)
+ if ((number = (CFNumberRef)CFDictionaryGetValue(
+ stats_dict,
+ CFSTR(kIOBlockStorageDriverStatisticsTotalReadTimeKey))))
+ {
+ CFNumberGetValue(number, kCFNumberSInt64Type, &read_time);
+ }
+ if ((number = (CFNumberRef)CFDictionaryGetValue(
+ stats_dict,
+ CFSTR(kIOBlockStorageDriverStatisticsTotalWriteTimeKey))))
+ {
+ CFNumberGetValue(number, kCFNumberSInt64Type, &write_time);
+ }
+
+ // Read/Write time on macOS comes back in nanoseconds and in psutil
+ // we've standardized on milliseconds so do the conversion.
+ py_disk_info = Py_BuildValue(
+ "(KKKKKK)",
+ reads,
+ writes,
+ read_bytes,
+ write_bytes,
+ read_time / 1000 / 1000,
+ write_time / 1000 / 1000);
+ if (!py_disk_info)
+ goto error;
+ if (PyDict_SetItemString(py_retdict, disk_name, py_disk_info))
+ goto error;
+ Py_CLEAR(py_disk_info);
+
+ CFRelease(parent_dict);
+ IOObjectRelease(parent);
+ CFRelease(props_dict);
+ IOObjectRelease(disk);
+ }
+ }
+
+ IOObjectRelease (disk_list);
+
+ return py_retdict;
+
+error:
+ Py_XDECREF(py_disk_info);
+ Py_DECREF(py_retdict);
+ return NULL;
+}
diff --git a/psutil/arch/osx/disk.h b/psutil/arch/osx/disk.h
new file mode 100644
index 00000000..88ca9a28
--- /dev/null
+++ b/psutil/arch/osx/disk.h
@@ -0,0 +1,11 @@
+/*
+ * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <Python.h>
+
+PyObject *psutil_disk_io_counters(PyObject *self, PyObject *args);
+PyObject *psutil_disk_partitions(PyObject *self, PyObject *args);
+PyObject *psutil_disk_usage_used(PyObject *self, PyObject *args);
diff --git a/psutil/arch/osx/mem.c b/psutil/arch/osx/mem.c
new file mode 100644
index 00000000..53493065
--- /dev/null
+++ b/psutil/arch/osx/mem.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// System memory related functions. Original code was refactored and moved
+// from psutil/_psutil_osx.c in 2023. This is the GIT blame before the move:
+// https://github.com/giampaolo/psutil/blame/efd7ed3/psutil/_psutil_osx.c
+
+#include <Python.h>
+#include <mach/host_info.h>
+#include <sys/sysctl.h>
+#include <mach/mach.h>
+
+#include "../../_psutil_posix.h"
+
+
+static int
+psutil_sys_vminfo(vm_statistics_data_t *vmstat) {
+ kern_return_t ret;
+ mach_msg_type_number_t count = sizeof(*vmstat) / sizeof(integer_t);
+ mach_port_t mport = mach_host_self();
+
+ ret = host_statistics(mport, HOST_VM_INFO, (host_info_t)vmstat, &count);
+ if (ret != KERN_SUCCESS) {
+ PyErr_Format(
+ PyExc_RuntimeError,
+ "host_statistics(HOST_VM_INFO) syscall failed: %s",
+ mach_error_string(ret));
+ return 0;
+ }
+ mach_port_deallocate(mach_task_self(), mport);
+ return 1;
+}
+
+
+/*
+ * Return system virtual memory stats.
+ * See:
+ * https://opensource.apple.com/source/system_cmds/system_cmds-790/
+ * vm_stat.tproj/vm_stat.c.auto.html
+ */
+PyObject *
+psutil_virtual_mem(PyObject *self, PyObject *args) {
+ int mib[2];
+ uint64_t total;
+ size_t len = sizeof(total);
+ vm_statistics_data_t vm;
+ long pagesize = psutil_getpagesize();
+ // physical mem
+ mib[0] = CTL_HW;
+ mib[1] = HW_MEMSIZE;
+
+ // This is also available as sysctlbyname("hw.memsize").
+ if (sysctl(mib, 2, &total, &len, NULL, 0)) {
+ if (errno != 0)
+ PyErr_SetFromErrno(PyExc_OSError);
+ else
+ PyErr_Format(
+ PyExc_RuntimeError, "sysctl(HW_MEMSIZE) syscall failed");
+ return NULL;
+ }
+
+ // vm
+ if (!psutil_sys_vminfo(&vm))
+ return NULL;
+
+ return Py_BuildValue(
+ "KKKKKK",
+ total,
+ (unsigned long long) vm.active_count * pagesize, // active
+ (unsigned long long) vm.inactive_count * pagesize, // inactive
+ (unsigned long long) vm.wire_count * pagesize, // wired
+ (unsigned long long) vm.free_count * pagesize, // free
+ (unsigned long long) vm.speculative_count * pagesize // speculative
+ );
+}
+
+
+/*
+ * Return stats about swap memory.
+ */
+PyObject *
+psutil_swap_mem(PyObject *self, PyObject *args) {
+ int mib[2];
+ size_t size;
+ struct xsw_usage totals;
+ vm_statistics_data_t vmstat;
+ long pagesize = psutil_getpagesize();
+
+ mib[0] = CTL_VM;
+ mib[1] = VM_SWAPUSAGE;
+ size = sizeof(totals);
+ if (sysctl(mib, 2, &totals, &size, NULL, 0) == -1) {
+ if (errno != 0)
+ PyErr_SetFromErrno(PyExc_OSError);
+ else
+ PyErr_Format(
+ PyExc_RuntimeError, "sysctl(VM_SWAPUSAGE) syscall failed");
+ return NULL;
+ }
+ if (!psutil_sys_vminfo(&vmstat))
+ return NULL;
+
+ return Py_BuildValue(
+ "LLLKK",
+ totals.xsu_total,
+ totals.xsu_used,
+ totals.xsu_avail,
+ (unsigned long long)vmstat.pageins * pagesize,
+ (unsigned long long)vmstat.pageouts * pagesize);
+}
diff --git a/psutil/arch/osx/mem.h b/psutil/arch/osx/mem.h
new file mode 100644
index 00000000..dc4cd743
--- /dev/null
+++ b/psutil/arch/osx/mem.h
@@ -0,0 +1,10 @@
+/*
+ * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <Python.h>
+
+PyObject *psutil_swap_mem(PyObject *self, PyObject *args);
+PyObject *psutil_virtual_mem(PyObject *self, PyObject *args);
diff --git a/psutil/arch/osx/net.c b/psutil/arch/osx/net.c
new file mode 100644
index 00000000..e9cc61e9
--- /dev/null
+++ b/psutil/arch/osx/net.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// Networks related functions. Original code was refactored and moved
+// from psutil/_psutil_osx.c in 2023. This is the GIT blame before the move:
+// https://github.com/giampaolo/psutil/blame/efd7ed3/psutil/_psutil_osx.c
+
+#include <Python.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+#include <sys/sysctl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+
+#include "../../_psutil_common.h"
+
+
+PyObject *
+psutil_net_io_counters(PyObject *self, PyObject *args) {
+ char *buf = NULL, *lim, *next;
+ struct if_msghdr *ifm;
+ int mib[6];
+ mib[0] = CTL_NET; // networking subsystem
+ mib[1] = PF_ROUTE; // type of information
+ mib[2] = 0; // protocol (IPPROTO_xxx)
+ mib[3] = 0; // address family
+ mib[4] = NET_RT_IFLIST2; // operation
+ mib[5] = 0;
+ size_t len;
+ PyObject *py_ifc_info = NULL;
+ PyObject *py_retdict = PyDict_New();
+
+ if (py_retdict == NULL)
+ return NULL;
+
+ if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+
+ buf = malloc(len);
+ if (buf == NULL) {
+ PyErr_NoMemory();
+ goto error;
+ }
+
+ if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+
+ lim = buf + len;
+
+ for (next = buf; next < lim; ) {
+ ifm = (struct if_msghdr *)next;
+ next += ifm->ifm_msglen;
+
+ if (ifm->ifm_type == RTM_IFINFO2) {
+ py_ifc_info = NULL;
+ struct if_msghdr2 *if2m = (struct if_msghdr2 *)ifm;
+ struct sockaddr_dl *sdl = (struct sockaddr_dl *)(if2m + 1);
+ char ifc_name[32];
+
+ strncpy(ifc_name, sdl->sdl_data, sdl->sdl_nlen);
+ ifc_name[sdl->sdl_nlen] = 0;
+
+ py_ifc_info = Py_BuildValue(
+ "(KKKKKKKi)",
+ if2m->ifm_data.ifi_obytes,
+ if2m->ifm_data.ifi_ibytes,
+ if2m->ifm_data.ifi_opackets,
+ if2m->ifm_data.ifi_ipackets,
+ if2m->ifm_data.ifi_ierrors,
+ if2m->ifm_data.ifi_oerrors,
+ if2m->ifm_data.ifi_iqdrops,
+ 0); // dropout not supported
+
+ if (!py_ifc_info)
+ goto error;
+ if (PyDict_SetItemString(py_retdict, ifc_name, py_ifc_info))
+ goto error;
+ Py_CLEAR(py_ifc_info);
+ }
+ else {
+ continue;
+ }
+ }
+
+ free(buf);
+ return py_retdict;
+
+error:
+ Py_XDECREF(py_ifc_info);
+ Py_DECREF(py_retdict);
+ if (buf != NULL)
+ free(buf);
+ return NULL;
+}
diff --git a/psutil/arch/osx/net.h b/psutil/arch/osx/net.h
new file mode 100644
index 00000000..99079523
--- /dev/null
+++ b/psutil/arch/osx/net.h
@@ -0,0 +1,9 @@
+/*
+ * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <Python.h>
+
+PyObject *psutil_net_io_counters(PyObject *self, PyObject *args);
diff --git a/psutil/arch/osx/sensors.c b/psutil/arch/osx/sensors.c
new file mode 100644
index 00000000..a2faa157
--- /dev/null
+++ b/psutil/arch/osx/sensors.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// Sensors related functions. Original code was refactored and moved
+// from psutil/_psutil_osx.c in 2023. This is the GIT blame before the move:
+// https://github.com/giampaolo/psutil/blame/efd7ed3/psutil/_psutil_osx.c
+// Original battery code:
+// https://github.com/giampaolo/psutil/commit/e0df5da
+
+
+#include <Python.h>
+#include <IOKit/ps/IOPowerSources.h>
+#include <IOKit/ps/IOPSKeys.h>
+
+#include "../../_psutil_common.h"
+
+
+PyObject *
+psutil_sensors_battery(PyObject *self, PyObject *args) {
+ PyObject *py_tuple = NULL;
+ CFTypeRef power_info = NULL;
+ CFArrayRef power_sources_list = NULL;
+ CFDictionaryRef power_sources_information = NULL;
+ CFNumberRef capacity_ref = NULL;
+ CFNumberRef time_to_empty_ref = NULL;
+ CFStringRef ps_state_ref = NULL;
+ uint32_t capacity; /* units are percent */
+ int time_to_empty; /* units are minutes */
+ int is_power_plugged;
+
+ power_info = IOPSCopyPowerSourcesInfo();
+
+ if (!power_info) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "IOPSCopyPowerSourcesInfo() syscall failed");
+ goto error;
+ }
+
+ power_sources_list = IOPSCopyPowerSourcesList(power_info);
+ if (!power_sources_list) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "IOPSCopyPowerSourcesList() syscall failed");
+ goto error;
+ }
+
+ /* Should only get one source. But in practice, check for > 0 sources */
+ if (!CFArrayGetCount(power_sources_list)) {
+ PyErr_SetString(PyExc_NotImplementedError, "no battery");
+ goto error;
+ }
+
+ power_sources_information = IOPSGetPowerSourceDescription(
+ power_info, CFArrayGetValueAtIndex(power_sources_list, 0));
+
+ capacity_ref = (CFNumberRef) CFDictionaryGetValue(
+ power_sources_information, CFSTR(kIOPSCurrentCapacityKey));
+ if (!CFNumberGetValue(capacity_ref, kCFNumberSInt32Type, &capacity)) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "No battery capacity infomration in power sources info");
+ goto error;
+ }
+
+ ps_state_ref = (CFStringRef) CFDictionaryGetValue(
+ power_sources_information, CFSTR(kIOPSPowerSourceStateKey));
+ is_power_plugged = CFStringCompare(
+ ps_state_ref, CFSTR(kIOPSACPowerValue), 0)
+ == kCFCompareEqualTo;
+
+ time_to_empty_ref = (CFNumberRef) CFDictionaryGetValue(
+ power_sources_information, CFSTR(kIOPSTimeToEmptyKey));
+ if (!CFNumberGetValue(time_to_empty_ref,
+ kCFNumberIntType, &time_to_empty)) {
+ /* This value is recommended for non-Apple power sources, so it's not
+ * an error if it doesn't exist. We'll return -1 for "unknown" */
+ /* A value of -1 indicates "Still Calculating the Time" also for
+ * apple power source */
+ time_to_empty = -1;
+ }
+
+ py_tuple = Py_BuildValue("Iii",
+ capacity, time_to_empty, is_power_plugged);
+ if (!py_tuple) {
+ goto error;
+ }
+
+ CFRelease(power_info);
+ CFRelease(power_sources_list);
+ /* Caller should NOT release power_sources_information */
+
+ return py_tuple;
+
+error:
+ if (power_info)
+ CFRelease(power_info);
+ if (power_sources_list)
+ CFRelease(power_sources_list);
+ Py_XDECREF(py_tuple);
+ return NULL;
+}
diff --git a/psutil/arch/osx/sensors.h b/psutil/arch/osx/sensors.h
new file mode 100644
index 00000000..edace25d
--- /dev/null
+++ b/psutil/arch/osx/sensors.h
@@ -0,0 +1,9 @@
+/*
+ * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <Python.h>
+
+PyObject *psutil_sensors_battery(PyObject *self, PyObject *args);
diff --git a/psutil/arch/osx/sys.c b/psutil/arch/osx/sys.c
new file mode 100644
index 00000000..4fe66425
--- /dev/null
+++ b/psutil/arch/osx/sys.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// System related functions. Original code was refactored and moved
+// from psutil/_psutil_osx.c in 2023. This is the GIT blame before the move:
+// https://github.com/giampaolo/psutil/blame/efd7ed3/psutil/_psutil_osx.c
+
+#include <Python.h>
+#include <sys/sysctl.h>
+#include <utmpx.h>
+
+#include "../../_psutil_common.h"
+
+
+PyObject *
+psutil_boot_time(PyObject *self, PyObject *args) {
+ // fetch sysctl "kern.boottime"
+ static int request[2] = { CTL_KERN, KERN_BOOTTIME };
+ struct timeval result;
+ size_t result_len = sizeof result;
+ time_t boot_time = 0;
+
+ if (sysctl(request, 2, &result, &result_len, NULL, 0) == -1)
+ return PyErr_SetFromErrno(PyExc_OSError);
+ boot_time = result.tv_sec;
+ return Py_BuildValue("f", (float)boot_time);
+}
+
+
+PyObject *
+psutil_users(PyObject *self, PyObject *args) {
+ struct utmpx *utx;
+ PyObject *py_username = NULL;
+ PyObject *py_tty = NULL;
+ PyObject *py_hostname = NULL;
+ PyObject *py_tuple = NULL;
+ PyObject *py_retlist = PyList_New(0);
+
+ if (py_retlist == NULL)
+ return NULL;
+ while ((utx = getutxent()) != NULL) {
+ if (utx->ut_type != USER_PROCESS)
+ continue;
+ py_username = PyUnicode_DecodeFSDefault(utx->ut_user);
+ if (! py_username)
+ goto error;
+ py_tty = PyUnicode_DecodeFSDefault(utx->ut_line);
+ if (! py_tty)
+ goto error;
+ py_hostname = PyUnicode_DecodeFSDefault(utx->ut_host);
+ if (! py_hostname)
+ goto error;
+ py_tuple = Py_BuildValue(
+ "(OOOdi)",
+ py_username, // username
+ py_tty, // tty
+ py_hostname, // hostname
+ (double)utx->ut_tv.tv_sec, // start time
+ utx->ut_pid // process id
+ );
+ if (!py_tuple) {
+ endutxent();
+ goto error;
+ }
+ if (PyList_Append(py_retlist, py_tuple)) {
+ endutxent();
+ goto error;
+ }
+ Py_CLEAR(py_username);
+ Py_CLEAR(py_tty);
+ Py_CLEAR(py_hostname);
+ Py_CLEAR(py_tuple);
+ }
+
+ endutxent();
+ return py_retlist;
+
+error:
+ Py_XDECREF(py_username);
+ Py_XDECREF(py_tty);
+ Py_XDECREF(py_hostname);
+ Py_XDECREF(py_tuple);
+ Py_DECREF(py_retlist);
+ return NULL;
+}
diff --git a/psutil/arch/osx/sys.h b/psutil/arch/osx/sys.h
new file mode 100644
index 00000000..344ca21d
--- /dev/null
+++ b/psutil/arch/osx/sys.h
@@ -0,0 +1,10 @@
+/*
+ * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <Python.h>
+
+PyObject *psutil_boot_time(PyObject *self, PyObject *args);
+PyObject *psutil_users(PyObject *self, PyObject *args);
diff --git a/setup.py b/setup.py
index ed379f7e..c3153a1e 100755
--- a/setup.py
+++ b/setup.py
@@ -243,6 +243,11 @@ elif MACOS:
'psutil/_psutil_osx.c',
'psutil/arch/osx/process_info.c',
'psutil/arch/osx/cpu.c',
+ 'psutil/arch/osx/disk.c',
+ 'psutil/arch/osx/mem.c',
+ 'psutil/arch/osx/net.c',
+ 'psutil/arch/osx/sensors.c',
+ 'psutil/arch/osx/sys.c',
],
define_macros=macros,
extra_link_args=[