summaryrefslogtreecommitdiff
path: root/psutil/arch/osx
diff options
context:
space:
mode:
Diffstat (limited to 'psutil/arch/osx')
-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
12 files changed, 900 insertions, 6 deletions
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);