diff --git a/scripts/disk_usage.nim b/scripts/disk_usage.nim index 811d5a3..916396b 100644 --- a/scripts/disk_usage.nim +++ b/scripts/disk_usage.nim @@ -12,11 +12,11 @@ import strformat import psutil proc main() = - var n: int = max(mapIt(psutil.disk_partitions(all=false), len(it.device))) + var n: int = max(mapIt(psutil.disk_partitions(all = false), len(it.device))) echo(&"""{alignString("Device", n)} {"Total":>10} {"Used":>10} """ & &"""{"Free":>10} {"Use":>5}% {"Type":>9} Mount""") - for part in psutil.disk_partitions(all=false): + for part in psutil.disk_partitions(all = false): when defined(windows): if "cdrom" in part.opts or part.fstype == "": # skip cd-rom drives with no disk in it; they may raise diff --git a/scripts/free.nim b/scripts/free.nim index f2eda38..55f3a2f 100644 --- a/scripts/free.nim +++ b/scripts/free.nim @@ -9,15 +9,15 @@ import strformat import psutil proc main() = - let virt = psutil.virtual_memory() - let swap = psutil.swap_memory() - echo(&"""{"total":>17} {"used":>10} {"free":>10} {"shared":>10} """ & - &"""{"buffers":>10} {"cache":>10}""") - echo(&"Mem: {int(virt.total / 1024):>12} {int(virt.used / 1024):>10} " & - &"{int(virt.free / 1024):>10} {int(virt.shared / 1024):>10} " & - &"{int(virt.buffers / 1024):>10} {int(virt.cached / 1024):>10}") - echo(&"Swap: {int(swap.total / 1024):>11} {int(swap.used / 1024):>10} " & - &"{int(swap.free / 1024):>10} ") + let virt = psutil.virtual_memory() + let swap = psutil.swap_memory() + echo(&"""{"total":>17} {"used":>10} {"free":>10} {"shared":>10} """ & + &"""{"buffers":>10} {"cache":>10}""") + echo(&"Mem: {int(virt.total / 1024):>12} {int(virt.used / 1024):>10} " & + &"{int(virt.free / 1024):>10} {int(virt.shared / 1024):>10} " & + &"{int(virt.buffers / 1024):>10} {int(virt.cached / 1024):>10}") + echo(&"Swap: {int(swap.total / 1024):>11} {int(swap.used / 1024):>10} " & + &"{int(swap.free / 1024):>10} ") when isMainModule: - main() + main() diff --git a/scripts/ifconfig.nim b/scripts/ifconfig.nim index 76a6da5..e04e6a1 100644 --- a/scripts/ifconfig.nim +++ b/scripts/ifconfig.nim @@ -34,9 +34,9 @@ import sequtils import tables when defined(posix): - import posix + import posix else: - import winlean + import winlean import stringinterpolation @@ -57,37 +57,38 @@ var duplex_map = { proc main() = - let stats = psutil.net_if_stats() - let io_counters = psutil.per_nic_net_io_counters() + let stats = psutil.net_if_stats() + let io_counters = psutil.per_nic_net_io_counters() - for nic, addrs in psutil.net_if_addrs(): - echo("%s:".format(nic)) - if nic in stats: - let st = stats[nic] - stdout.write(" stats : ") - echo("speed=%sMB, duplex=%s, mtu=%s, up=%s".format( - st.speed, duplex_map[st.duplex], st.mtu, - if st.isup: "yes" else: "no")) - if nic in io_counters: - let io = io_counters[nic] - stdout.write(" incoming : ") - echo("bytes=%s, pkts=%s, errs=%s, drops=%s".format( - io.bytes_recv, io.packets_recv, io.errin, io.dropin)) - stdout.write(" outgoing : ") - echo("bytes=%s, pkts=%s, errs=%s, drops=%s".format( - io.bytes_sent, io.packets_sent, io.errout, io.dropout)) - for addr in addrs: - if addr.address != nil: - stdout.write( " %-4s".format(af_map.mgetOrPut(addr.family.cint, $addr.family)) ) - echo(" address : %s".format(addr.address)) - if addr.broadcast != nil: - echo(" broadcast : %s".format(addr.broadcast)) - if addr.netmask != nil: - echo(" netmask : %s".format(addr.netmask)) - if addr.ptp != nil: - echo(" p2p : %s".format(addr.ptp)) - echo("") + for nic, addrs in psutil.net_if_addrs(): + echo("%s:".format(nic)) + if nic in stats: + let st = stats[nic] + stdout.write(" stats : ") + echo("speed=%sMB, duplex=%s, mtu=%s, up=%s".format( + st.speed, duplex_map[st.duplex], st.mtu, + if st.isup: "yes" else: "no")) + if nic in io_counters: + let io = io_counters[nic] + stdout.write(" incoming : ") + echo("bytes=%s, pkts=%s, errs=%s, drops=%s".format( + io.bytes_recv, io.packets_recv, io.errin, io.dropin)) + stdout.write(" outgoing : ") + echo("bytes=%s, pkts=%s, errs=%s, drops=%s".format( + io.bytes_sent, io.packets_sent, io.errout, io.dropout)) + for addr in addrs: + if addr.address != nil: + stdout.write(" %-4s".format(af_map.mgetOrPut(addr.family.cint, + $addr.family))) + echo(" address : %s".format(addr.address)) + if addr.broadcast != nil: + echo(" broadcast : %s".format(addr.broadcast)) + if addr.netmask != nil: + echo(" netmask : %s".format(addr.netmask)) + if addr.ptp != nil: + echo(" p2p : %s".format(addr.ptp)) + echo("") when isMainModule: - main() + main() diff --git a/scripts/meminfo.nim b/scripts/meminfo.nim index d992afa..61cb790 100644 --- a/scripts/meminfo.nim +++ b/scripts/meminfo.nim @@ -25,7 +25,7 @@ import strutils import strformat import psutil -proc pprint_object[T]( obj: T ): void = +proc pprint_object[T](obj: T): void = var n: string var v: float for name, value in obj.fieldPairs: diff --git a/src/psutil.nim b/src/psutil.nim index d94380e..b008d55 100644 --- a/src/psutil.nim +++ b/src/psutil.nim @@ -27,245 +27,245 @@ when defined(macosx): var g_last_cpu_times: CPUTimes var g_last_per_cpu_times: seq[CPUTimes] try: - g_last_cpu_times = cpu_times() - g_last_per_cpu_times = per_cpu_times() + g_last_cpu_times = cpu_times() + g_last_per_cpu_times = per_cpu_times() except IOError: - discard + discard var g_total_phymem: int ################################################################################ -proc pid_exists*( pid: int ): bool = - ## Return True if given PID exists in the current process list. - ## This is faster than doing "pid in psutil.pids()" and should be preferred. +proc pid_exists*(pid: int): bool = + ## Return True if given PID exists in the current process list. + ## This is faster than doing "pid in psutil.pids()" and should be preferred. - if pid < 0: - return false + if pid < 0: + return false - elif pid == 0 and defined(posix): - # On POSIX we use os.kill() to determine PID existence. - # According to "man 2 kill" PID 0 has a special meaning - # though: it refers to <> and that is not we want - # to do here. - return pid in pids() + elif pid == 0 and defined(posix): + # On POSIX we use os.kill() to determine PID existence. + # According to "man 2 kill" PID 0 has a special meaning + # though: it refers to <> and that is not we want + # to do here. + return pid in pids() - else: - return platform.pid_exists(pid) + else: + return platform.pid_exists(pid) -proc cpu_count*(logical=true): int = - # Return the number of logical CPUs in the system. - # If logical is False return the number of physical cores only - # (e.g. hyper thread CPUs are excluded). - # Return 0 if undetermined. - if logical: platform.cpu_count_logical().int - else: platform.cpu_count_physical() +proc cpu_count*(logical = true): int = + # Return the number of logical CPUs in the system. + # If logical is False return the number of physical cores only + # (e.g. hyper thread CPUs are excluded). + # Return 0 if undetermined. + if logical: platform.cpu_count_logical().int + else: platform.cpu_count_physical() proc calculate(t1, t2: CPUTimes): float = - when defined(windows): - let t1_all = t1.user + t1.system + t1.idle + t1.interrupt + t1.dpc - else: - let t1_all = t1.user + t1.nice + t1.system + t1.idle + t1.iowait + - t1.irq + t1.softirq + t1.steal + t1.guest + t1.guest_nice - let t1_busy = t1_all - t1.idle - - when defined(windows): - let t2_all = t2.user + t2.system + t2.idle + t2.interrupt + t2.dpc - else: - let t2_all = t2.user + t2.nice + t2.system + t2.idle + t2.iowait + - t2.irq + t2.softirq + t2.steal + t2.guest + t2.guest_nice - let t2_busy = t2_all - t2.idle - - # this usually indicates a float precision issue - if t2_busy <= t1_busy: - return 0.0 - - let busy_delta = t2_busy - t1_busy - let all_delta = t2_all - t1_all - let busy_perc = ( busy_delta / all_delta ) * 100 - return round( busy_perc, 1 ) - - -proc cpu_percent*( interval=0.0 ): float = - ## Return a float representing the current system-wide CPU utilization as a percentage. - ## - ## When interval is > 0.0 compares system CPU times elapsed before - ## and after the interval (blocking). - ## - ## When interval is == 0.0 compares system CPU times elapsed since last - ## call or module import, returning immediately (non - ## blocking). That means the first time this is called it will - ## return a meaningless 0.0 value which you should ignore. - ## In this case is recommended for accuracy that this function be - ## called with at least 0.1 seconds between calls. - ## When percpu is True returns a list of floats representing the - ## utilization as a percentage for each CPU. - ## First element of the list refers to first CPU, second element - ## to second CPU and so on. - ## The order of the list is consistent across calls. - ## Examples: - ## >>> # blocking, system-wide - ## >>> psutil.cpu_percent(interval=1) - ## 2.0 - ## >>> - ## >>> # blocking, per-cpu - ## >>> psutil.cpu_percent(interval=1, percpu=True) - ## [2.0, 1.0] - ## >>> - ## >>> # non-blocking (percentage since last call) - ## >>> psutil.cpu_percent(interval=None) - ## 2.9 - ## >>> - - let blocking = interval > 0.0 - if interval < 0: - raise newException(ValueError, "interval is not positive (got $1)" % $interval) - - # system-wide usage - var t1 = g_last_cpu_times - if blocking: - t1 = cpu_times() - sleep( int(interval * 1000) ) - else: - var empty: CPUTimes - if t1 == empty: - # Something bad happened at import time. We'll - # get a meaningful result on the next call. See: - # https://github.com/giampaolo/psutil/pull/715 - t1 = cpu_times() - g_last_cpu_times = cpu_times() - return calculate(t1, g_last_cpu_times) - - -proc per_cpu_percent*( interval=0.0 ): seq[float] = - let blocking = interval > 0.0 - if interval < 0: - raise newException(ValueError, "interval is not positive (got $1)" % $interval) - - result = newSeq[float]() - var tot1 = g_last_per_cpu_times - if blocking: - tot1 = per_cpu_times() - sleep( int( interval * 1000 ) ) - else: - if not tot1.len > 0: - # Something bad happened at import time. We'll - # get a meaningful result on the next call. See: - # https://github.com/giampaolo/psutil/pull/715 - tot1 = per_cpu_times() - - g_last_per_cpu_times = per_cpu_times() - for pair in zip(tot1, g_last_per_cpu_times): - result.add(calculate(pair[0], pair[1])) - return result + when defined(windows): + let t1_all = t1.user + t1.system + t1.idle + t1.interrupt + t1.dpc + else: + let t1_all = t1.user + t1.nice + t1.system + t1.idle + t1.iowait + + t1.irq + t1.softirq + t1.steal + t1.guest + t1.guest_nice + let t1_busy = t1_all - t1.idle + + when defined(windows): + let t2_all = t2.user + t2.system + t2.idle + t2.interrupt + t2.dpc + else: + let t2_all = t2.user + t2.nice + t2.system + t2.idle + t2.iowait + + t2.irq + t2.softirq + t2.steal + t2.guest + t2.guest_nice + let t2_busy = t2_all - t2.idle + + # this usually indicates a float precision issue + if t2_busy <= t1_busy: + return 0.0 + + let busy_delta = t2_busy - t1_busy + let all_delta = t2_all - t1_all + let busy_perc = (busy_delta / all_delta) * 100 + return round(busy_perc, 1) + + +proc cpu_percent*(interval = 0.0): float = + ## Return a float representing the current system-wide CPU utilization as a percentage. + ## + ## When interval is > 0.0 compares system CPU times elapsed before + ## and after the interval (blocking). + ## + ## When interval is == 0.0 compares system CPU times elapsed since last + ## call or module import, returning immediately (non + ## blocking). That means the first time this is called it will + ## return a meaningless 0.0 value which you should ignore. + ## In this case is recommended for accuracy that this function be + ## called with at least 0.1 seconds between calls. + ## When percpu is True returns a list of floats representing the + ## utilization as a percentage for each CPU. + ## First element of the list refers to first CPU, second element + ## to second CPU and so on. + ## The order of the list is consistent across calls. + ## Examples: + ## >>> # blocking, system-wide + ## >>> psutil.cpu_percent(interval=1) + ## 2.0 + ## >>> + ## >>> # blocking, per-cpu + ## >>> psutil.cpu_percent(interval=1, percpu=True) + ## [2.0, 1.0] + ## >>> + ## >>> # non-blocking (percentage since last call) + ## >>> psutil.cpu_percent(interval=None) + ## 2.9 + ## >>> + + let blocking = interval > 0.0 + if interval < 0: + raise newException(ValueError, "interval is not positive (got $1)" % $interval) + + # system-wide usage + var t1 = g_last_cpu_times + if blocking: + t1 = cpu_times() + sleep(int(interval * 1000)) + else: + var empty: CPUTimes + if t1 == empty: + # Something bad happened at import time. We'll + # get a meaningful result on the next call. See: + # https://github.com/giampaolo/psutil/pull/715 + t1 = cpu_times() + g_last_cpu_times = cpu_times() + return calculate(t1, g_last_cpu_times) + + +proc per_cpu_percent*(interval = 0.0): seq[float] = + let blocking = interval > 0.0 + if interval < 0: + raise newException(ValueError, "interval is not positive (got $1)" % $interval) + + result = newSeq[float]() + var tot1 = g_last_per_cpu_times + if blocking: + tot1 = per_cpu_times() + sleep(int(interval * 1000)) + else: + if not tot1.len > 0: + # Something bad happened at import time. We'll + # get a meaningful result on the next call. See: + # https://github.com/giampaolo/psutil/pull/715 + tot1 = per_cpu_times() + + g_last_per_cpu_times = per_cpu_times() + for pair in zip(tot1, g_last_per_cpu_times): + result.add(calculate(pair[0], pair[1])) + return result proc virtual_memory*(): VirtualMemory = - ## Return statistics about system memory usage as a namedtuple - ## including the following fields, expressed in bytes: - ## - total: - ## total physical memory available. - ## - available: - ## the memory that can be given instantly to processes without the - ## system going into swap. - ## This is calculated by summing different memory values depending - ## on the platform and it is supposed to be used to monitor actual - ## memory usage in a cross platform fashion. - ## - percent: - ## the percentage usage calculated as (total - available) / total * 100 - ## - used: - ## memory used, calculated differently depending on the platform and - ## designed for informational purposes only: - ## OSX: active + inactive + wired - ## BSD: active + wired + cached - ## LINUX: total - free - ## - free: - ## memory not being used at all (zeroed) that is readily available; - ## note that this doesn't reflect the actual memory available - ## (use 'available' instead) - ## Platform-specific fields: - ## - active (UNIX): - ## memory currently in use or very recently used, and so it is in RAM. - ## - inactive (UNIX): - ## memory that is marked as not used. - ## - buffers (BSD, Linux): - ## cache for things like file system metadata. - ## - cached (BSD, OSX): - ## cache for various things. - ## - wired (OSX, BSD): - ## memory that is marked to always stay in RAM. It is never moved to disk. - ## - shared (BSD): - ## memory that may be simultaneously accessed by multiple processes. - ## The sum of 'used' and 'available' does not necessarily equal total. - ## On Windows 'available' and 'free' are the same. - - result = platform.virtual_memory() - # cached for later use in Process.memory_percent() - g_total_phymem = result.total + ## Return statistics about system memory usage as a namedtuple + ## including the following fields, expressed in bytes: + ## - total: + ## total physical memory available. + ## - available: + ## the memory that can be given instantly to processes without the + ## system going into swap. + ## This is calculated by summing different memory values depending + ## on the platform and it is supposed to be used to monitor actual + ## memory usage in a cross platform fashion. + ## - percent: + ## the percentage usage calculated as (total - available) / total * 100 + ## - used: + ## memory used, calculated differently depending on the platform and + ## designed for informational purposes only: + ## OSX: active + inactive + wired + ## BSD: active + wired + cached + ## LINUX: total - free + ## - free: + ## memory not being used at all (zeroed) that is readily available; + ## note that this doesn't reflect the actual memory available + ## (use 'available' instead) + ## Platform-specific fields: + ## - active (UNIX): + ## memory currently in use or very recently used, and so it is in RAM. + ## - inactive (UNIX): + ## memory that is marked as not used. + ## - buffers (BSD, Linux): + ## cache for things like file system metadata. + ## - cached (BSD, OSX): + ## cache for various things. + ## - wired (OSX, BSD): + ## memory that is marked to always stay in RAM. It is never moved to disk. + ## - shared (BSD): + ## memory that may be simultaneously accessed by multiple processes. + ## The sum of 'used' and 'available' does not necessarily equal total. + ## On Windows 'available' and 'free' are the same. + + result = platform.virtual_memory() + # cached for later use in Process.memory_percent() + g_total_phymem = result.total proc net_io_counters*(): NetIO = - ## Return total network I/O statistics including the following fields: - ## - bytes_sent: number of bytes sent - ## - bytes_recv: number of bytes received - ## - packets_sent: number of packets sent - ## - packets_recv: number of packets received - ## - errin: total number of errors while receiving - ## - errout: total number of errors while sending - ## - dropin: total number of incoming packets which were dropped - ## - dropout: total number of outgoing packets which were dropped - ## (always 0 on OSX and BSD) - - let raw_counters = platform.per_nic_net_io_counters() - if len(raw_counters) == 0: - raise newException( Exception, "couldn't find any network interface") - - for _, counter in raw_counters: - result.bytes_sent += counter.bytes_sent - result.bytes_recv += counter.bytes_recv - result.packets_sent += counter.packets_sent - result.packets_recv += counter.packets_recv - result.errin += counter.errin - result.errout += counter.errout - result.dropin += counter.dropin - result.dropout += counter.dropout + ## Return total network I/O statistics including the following fields: + ## - bytes_sent: number of bytes sent + ## - bytes_recv: number of bytes received + ## - packets_sent: number of packets sent + ## - packets_recv: number of packets received + ## - errin: total number of errors while receiving + ## - errout: total number of errors while sending + ## - dropin: total number of incoming packets which were dropped + ## - dropout: total number of outgoing packets which were dropped + ## (always 0 on OSX and BSD) + + let raw_counters = platform.per_nic_net_io_counters() + if len(raw_counters) == 0: + raise newException(Exception, "couldn't find any network interface") + + for _, counter in raw_counters: + result.bytes_sent += counter.bytes_sent + result.bytes_recv += counter.bytes_recv + result.packets_sent += counter.packets_sent + result.packets_recv += counter.packets_recv + result.errin += counter.errin + result.errout += counter.errout + result.dropin += counter.dropin + result.dropout += counter.dropout proc disk_io_counters: DiskIO = - ## Return system disk I/O statistics as a namedtuple including - ## the following fields: - ## - read_count: number of reads - ## - write_count: number of writes - ## - read_bytes: number of bytes read - ## - write_bytes: number of bytes written - ## - read_time: time spent reading from disk (in milliseconds) - ## - write_time: time spent writing to disk (in milliseconds) - ## If perdisk is True return the same information for every - ## physical disk installed on the system as a dictionary - ## with partition names as the keys and the namedtuple - ## described above as the values. - ## On recent Windows versions 'diskperf -y' command may need to be - ## executed first otherwise this function won't find any disk. - - let counters = per_disk_io_counters() - if len( counters ) == 0: - raise newException( Exception, "couldn't find any physical disk") - - for counter in counters.values(): - result.read_count += counter.read_count - result.write_count += counter.write_count - result.read_bytes += counter.read_bytes - result.write_bytes += counter.write_bytes - result.read_time += counter.read_time - result.write_time += counter.write_time - - when defined(linux): - result.read_merged_count += counter.read_merged_count - result.write_merged_count += counter.write_merged_count - result.busy_time += counter.busy_time + ## Return system disk I/O statistics as a namedtuple including + ## the following fields: + ## - read_count: number of reads + ## - write_count: number of writes + ## - read_bytes: number of bytes read + ## - write_bytes: number of bytes written + ## - read_time: time spent reading from disk (in milliseconds) + ## - write_time: time spent writing to disk (in milliseconds) + ## If perdisk is True return the same information for every + ## physical disk installed on the system as a dictionary + ## with partition names as the keys and the namedtuple + ## described above as the values. + ## On recent Windows versions 'diskperf -y' command may need to be + ## executed first otherwise this function won't find any disk. + + let counters = per_disk_io_counters() + if len(counters) == 0: + raise newException(Exception, "couldn't find any physical disk") + + for counter in counters.values(): + result.read_count += counter.read_count + result.write_count += counter.write_count + result.read_bytes += counter.read_bytes + result.write_bytes += counter.write_bytes + result.read_time += counter.read_time + result.write_time += counter.write_time + + when defined(linux): + result.read_merged_count += counter.read_merged_count + result.write_merged_count += counter.write_merged_count + result.busy_time += counter.busy_time ################################################################################ @@ -292,4 +292,4 @@ export per_nic_net_io_counters export disk_io_counters export per_disk_io_counters export net_if_stats -export net_connections \ No newline at end of file +export net_connections diff --git a/src/psutil/arch/osx/process_info.nim b/src/psutil/arch/osx/process_info.nim index baff31c..f128f3a 100644 --- a/src/psutil/arch/osx/process_info.nim +++ b/src/psutil/arch/osx/process_info.nim @@ -1,13 +1,13 @@ import ./types import ./socket -const +const MAXPATHLEN = 1024 PROC_PIDLISTFDS* = 1.cint PROC_PIDLISTFD_SIZE* = 8.cint # defns of process file desc type https://opensource.apple.com/source/xnu/xnu-1456.1.26/bsd/sys/proc_info.h.auto.html -const +const PROX_FDTYPE_ATALK* = 0.cint PROX_FDTYPE_VNODE* = 1.cint PROX_FDTYPE_SOCKET* = 2.cint @@ -20,31 +20,38 @@ const PROC_PIDFDSOCKETINFO* = 3.cint -type proc_fdinfo* {.importc: "struct proc_fdinfo", header: "",pure.} = object +type proc_fdinfo* {.importc: "struct proc_fdinfo", header: "", + pure.} = object proc_fdtype*: cint proc_fd*: cint -type vnode_fdinfowithpath* {.importc: "struct vnode_fdinfowithpath", header: "".} = object +type vnode_fdinfowithpath* {.importc: "struct vnode_fdinfowithpath", + header: "".} = object -type proc_fileinfo* {.importc: "struct proc_fileinfo", header: "",pure.} = object +type proc_fileinfo* {.importc: "struct proc_fileinfo", + header: "", pure.} = object fi_openflags*: uint32 fi_status*: uint32 fi_offset*: off_t fi_guardflags*: uint32 -type socket_fdinfo* {.importc: "struct socket_fdinfo", header: "",pure.} = object +type socket_fdinfo* {.importc: "struct socket_fdinfo", + header: "", pure.} = object pfi*: proc_fileinfo psi*: socket_info # https://opensource.apple.com/source/xnu/xnu-2422.1.72/libsyscall/wrappers/libproc/libproc.h.auto.html -proc proc_pidinfo*(pid: cint, flavor: cint, arg: uint64, buffer: pointer, buffer_size: cint): cint {.importc: "proc_pidinfo", header: "".} +proc proc_pidinfo*(pid: cint, flavor: cint, arg: uint64, buffer: pointer, + buffer_size: cint): cint {.importc: "proc_pidinfo", header: "".} -proc proc_pidfdinfo*(pid: cint, flavor: cint, fd: cint, buffer: pointer, buffer_size: cint): cint {.importc: "proc_pidfdinfo", header: "".} +proc proc_pidfdinfo*(pid: cint, flavor: cint, fd: cint, buffer: pointer, + buffer_size: cint): cint {.importc: "proc_pidfdinfo", + header: "".} when isMainModule: @@ -75,5 +82,6 @@ when isMainModule: var z6 {.importc: "PROX_FDTYPE_FSEVENTS", header: "".}: cint assert z6 == PROX_FDTYPE_FSEVENTS - var z7 {.importc: "PROC_PIDFDVNODEPATHINFO", header: "".}: cint + var z7 {.importc: "PROC_PIDFDVNODEPATHINFO", + header: "".}: cint assert z7 == PROC_PIDFDVNODEPATHINFO diff --git a/src/psutil/arch/osx/socket.nim b/src/psutil/arch/osx/socket.nim index c46c642..7c484da 100644 --- a/src/psutil/arch/osx/socket.nim +++ b/src/psutil/arch/osx/socket.nim @@ -3,33 +3,33 @@ import ./types const TSI_T_NTIMERS = 4 -type vinfo_stat* = object # https://github.com/alecmocatta/socketstat/blob/062fae4f10d673fb447bfc9c5748f14dbd86c46d/src/mac.rs - vst_dev*: uint32 # [XSI] ID of device containing file - vst_mode*: uint16 # [XSI] Mode of file (see below) - vst_nlink*: uint16 # [XSI] Number of hard links - vst_ino*: uint64 # [XSI] File serial number +type vinfo_stat* = object # https://github.com/alecmocatta/socketstat/blob/062fae4f10d673fb447bfc9c5748f14dbd86c46d/src/mac.rs + vst_dev*: uint32 # [XSI] ID of device containing file + vst_mode*: uint16 # [XSI] Mode of file (see below) + vst_nlink*: uint16 # [XSI] Number of hard links + vst_ino*: uint64 # [XSI] File serial number vst_uid*: uid_t # [XSI] User ID of the file - vst_gid*: gid_t # [XSI] Group ID of the file - vst_atime*: int64 # [XSI] Time of last access - vst_atimensec*: int64 # nsec of last access - vst_mtime*: int64 # [XSI] Last data modification time - vst_mtimensec*: int64 # last data modification nsec - vst_ctime*: int64 # [XSI] Time of last status change - vst_ctimensec*: int64 # nsec of last status change - vst_birthtime*: int64 # File creation time(birth) - vst_birthtimensec*: int64 # nsec of File creation time - vst_size*: off_t # [XSI] file size, in bytes - vst_blocks*: int64 # [XSI] blocks allocated for file - vst_blksize*: int32 # [XSI] optimal blocksize for I/O - vst_flags*: uint32 # user defined flags for file - vst_gen*: uint32 # file generation number - vst_rdev*: uint32 # [XSI] Device ID - vst_qspare*: array[2, int64] # RESERVED: DO NOT USE! + vst_gid*: gid_t # [XSI] Group ID of the file + vst_atime*: int64 # [XSI] Time of last access + vst_atimensec*: int64 # nsec of last access + vst_mtime*: int64 # [XSI] Last data modification time + vst_mtimensec*: int64 # last data modification nsec + vst_ctime*: int64 # [XSI] Time of last status change + vst_ctimensec*: int64 # nsec of last status change + vst_birthtime*: int64 # File creation time(birth) + vst_birthtimensec*: int64 # nsec of File creation time + vst_size*: off_t # [XSI] file size, in bytes + vst_blocks*: int64 # [XSI] blocks allocated for file + vst_blksize*: int32 # [XSI] optimal blocksize for I/O + vst_flags*: uint32 # user defined flags for file + vst_gen*: uint32 # file generation number + vst_rdev*: uint32 # [XSI] Device ID + vst_qspare*: array[2, int64] # RESERVED: DO NOT USE! type sockbuf_info* = object sbi_cc*: uint32 - sbi_hiwat*: uint32 # SO_RCVBUF, SO_SNDBUF + sbi_hiwat*: uint32 # SO_RCVBUF, SO_SNDBUF sbi_mbcnt*: uint32 sbi_mbmax*: uint32 sbi_lowat*: uint32 @@ -38,7 +38,7 @@ type sockbuf_info* = object type insi_v4* = object - in4_tos*: cuchar # u_char type of service + in4_tos*: cuchar # u_char type of service type insi_v6* = object @@ -51,7 +51,8 @@ type in_addr = object s_addr: culong # load with inet_pton() -type in4in6_addr* {.importc: "struct in4in6_addr", header: "".} = object +type in4in6_addr* {.importc: "struct in4in6_addr", + header: "".} = object i46a_pad32*: array[3, uint32] i46a_addr4*: in_addr @@ -64,18 +65,19 @@ type address* {.importc: "struct addr", header: "".} = object ina_6*: in6_addr -type in_sockinfo* {.importc: "struct in_sockinfo", header: "".} = object - insi_fport*: cint # foreign port - insi_lport*: cint # local port - insi_gencnt*: uint64 # generation count of this instance - insi_flags*: uint32 # generic IP/datagram flags +type in_sockinfo* {.importc: "struct in_sockinfo", + header: "".} = object + insi_fport*: cint # foreign port + insi_lport*: cint # local port + insi_gencnt*: uint64 # generation count of this instance + insi_flags*: uint32 # generic IP/datagram flags insi_flow*: uint32 - insi_vflag*: uint8 # ini_IPV4 or ini_IPV6 - insi_ip_ttl*: uint8 # time to live proto - rfu_1*: uint32 # reserved - # protocol dependent part - insi_faddr*: address # foreign host table entry - insi_laddr*: address # local host table entry + insi_vflag*: uint8 # ini_IPV4 or ini_IPV6 + insi_ip_ttl*: uint8 # time to live proto + rfu_1*: uint32 # reserved + # protocol dependent part + insi_faddr*: address # foreign host table entry + insi_laddr*: address # local host table entry insi_v4*: insi_v4 insi_v6*: insi_v6 @@ -90,7 +92,8 @@ type tcp_sockinfo* = object tcpsi_tp*: uint64 # opaque handle of TCP protocol control block */ -type sockaddr_un* {.importc: "struct sockaddr_un", header: "", incompleteStruct, nodecl.} = object +type sockaddr_un* {.importc: "struct sockaddr_un", header: "", + incompleteStruct, nodecl.} = object sun_path*: ptr char @@ -102,9 +105,10 @@ type union_unsi_caddr = object ua_dummy*: array[SOCK_MAXADDRLEN, char] -type un_sockinfo* {.importc: "struct sockaddr_un", header: "", incompleteStruct, nodecl.} = object - unsi_conn_so*: uint64 # opaque handle of connected socket - unsi_conn_pcb*: uint64 # opaque handle of connected protocol control block +type un_sockinfo* {.importc: "struct sockaddr_un", header: "", + incompleteStruct, nodecl.} = object + unsi_conn_so*: uint64 # opaque handle of connected socket + unsi_conn_pcb*: uint64 # opaque handle of connected protocol control block unsi_addr*: union_unsi_caddr unsi_caddr*: union_unsi_caddr # union { @@ -127,10 +131,11 @@ type pri* = object hack_to_avoid_copying_more_structs*: array[524, uint8] -type socket_info* {.importc: "struct socket_info", header: "".} = object +type socket_info* {.importc: "struct socket_info", + header: "".} = object soi_stat*: vinfo_stat - soi_so*: uint64 # opaque handle of socket - soi_pcb*: uint64 # opaque handle of protocol control block + soi_so*: uint64 # opaque handle of socket + soi_pcb*: uint64 # opaque handle of protocol control block soi_type*: cint soi_protocol*: cint soi_family*: cint @@ -146,5 +151,5 @@ type socket_info* {.importc: "struct socket_info", header: "".} soi_rcv*: sockbuf_info soi_snd*: sockbuf_info soi_kind*: cint - rfu_1*: uint32 # reserved + rfu_1*: uint32 # reserved soi_proto*: pri diff --git a/src/psutil/arch/osx/types.nim b/src/psutil/arch/osx/types.nim index 0076ec9..f636659 100644 --- a/src/psutil/arch/osx/types.nim +++ b/src/psutil/arch/osx/types.nim @@ -4,4 +4,4 @@ type gid_t* = distinct uint32 type uid_t* = distinct uint32 -type off_t* {.importc:"off_t",header:"".} = object # https://stackoverflow.com/questions/9073667/where-to-find-the-complete-definition-of-off-t-type +type off_t* {.importc: "off_t", header: "".} = object # https://stackoverflow.com/questions/9073667/where-to-find-the-complete-definition-of-off-t-type diff --git a/src/psutil/common.nim b/src/psutil/common.nim index 3f5c86a..0e58a2e 100644 --- a/src/psutil/common.nim +++ b/src/psutil/common.nim @@ -2,109 +2,109 @@ import math, nativesockets, posix # type TSa_Family* {.importc: "sa_family_t", header: "".} = cint type Address* = object of RootObj - family*: posix.TSa_Family # int - address*: string - netmask*: string - broadcast*: string - ptp*: string + family*: posix.TSa_Family # int + address*: string + netmask*: string + broadcast*: string + ptp*: string type User* = object - name*: string - terminal*: string - host*: string - started*: float + name*: string + terminal*: string + host*: string + started*: float type CPUTimes* = object of RootObj - user*: float - system*: float - idle*: float - when defined(windows): - interrupt*: float - dpc*: float - when defined(posix): - nice*: float - iowait*: float - irq*: float - softirq*: float - steal*: float - guest*: float - guest_nice*: float - + user*: float + system*: float + idle*: float + when defined(windows): + interrupt*: float + dpc*: float + when defined(posix): + nice*: float + iowait*: float + irq*: float + softirq*: float + steal*: float + guest*: float + guest_nice*: float + type DiskUsage* = object of RootObj - total*: int - used*: int - free*:int - percent*: float + total*: int + used*: int + free*: int + percent*: float type DiskPartition* = object of RootObj - device*: string - mountpoint*: string - fstype*: string - opts*: string + device*: string + mountpoint*: string + fstype*: string + opts*: string type VirtualMemory* = object of RootObj - total*: int - avail*: int - percent*: float - used*: int - free*: int - active*: int - inactive*: int - buffers*: int - cached*: int - shared*: int + total*: int + avail*: int + percent*: float + used*: int + free*: int + active*: int + inactive*: int + buffers*: int + cached*: int + shared*: int type SwapMemory* = object of RootObj - total*: int - used*: int - free*: int - percent*: float - sin*: int - sout*: int + total*: int + used*: int + free*: int + percent*: float + sin*: int + sout*: int type NetIO* = object of RootObj - bytes_sent*: int - bytes_recv*: int - packets_sent*: int - packets_recv*: int - errin*: int - errout*: int - dropin*: int - dropout*: int + bytes_sent*: int + bytes_recv*: int + packets_sent*: int + packets_recv*: int + errin*: int + errout*: int + dropin*: int + dropout*: int type NicDuplex* = enum - NIC_DUPLEX_UNKNOWN, NIC_DUPLEX_HALF, NIC_DUPLEX_FULL + NIC_DUPLEX_UNKNOWN, NIC_DUPLEX_HALF, NIC_DUPLEX_FULL type NICStats* = object of RootObj - isup*: bool - duplex*: NicDuplex - speed*: int - mtu*: int + isup*: bool + duplex*: NicDuplex + speed*: int + mtu*: int type DiskIO* = object of RootObj - read_count*: int - write_count*: int - read_bytes*: int - write_bytes*: int - read_time*: int - write_time*: int - when defined(linux) or defined(macosx): - reads_merged*: int - read_merged_count*: int - write_merged_count*: int - busy_time*: int + read_count*: int + write_count*: int + read_bytes*: int + write_bytes*: int + read_time*: int + write_time*: int + when defined(linux) or defined(macosx): + reads_merged*: int + read_merged_count*: int + write_merged_count*: int + busy_time*: int type Connection* = object of RootObj - fd*: int - family*: int - `type`*: int - laddr*: string - lport*: Port - raddr*: string - rport*: Port - status*: string - pid*: int + fd*: int + family*: int + `type`*: int + laddr*: string + lport*: Port + raddr*: string + rport*: Port + status*: string + pid*: int proc usage_percent*[T](used: T, total: T, places = 0): float = diff --git a/src/psutil/psutil_linux.nim b/src/psutil/psutil_linux.nim index 49372fb..628b6b7 100644 --- a/src/psutil/psutil_linux.nim +++ b/src/psutil/psutil_linux.nim @@ -1,4 +1,4 @@ -{.deadCodeElim: on.} +{.deadCodeElim: on.} import algorithm, math, net, os, posix, sequtils, sets, strutils, tables, times import strformat import common, psutil_posix @@ -10,7 +10,7 @@ const PROCFS_PATH = "/proc" const UT_LINESIZE = 32 const UT_NAMESIZE = 32 const UT_HOSTSIZE = 256 -const USER_PROCESS = 7 # Normal process. +const USER_PROCESS = 7 # Normal process. const PATH_MAX = 4096 var MOUNTED {.header: "".}: cstring @@ -20,20 +20,20 @@ var DUPLEX_UNKNOWN {.header: "".}: uint8 var ETHTOOL_GSET {.header: "".}: uint8 var SIOCETHTOOL {.header: "".}: uint16 -let tcp4 = ( "tcp", posix.AF_INET, posix.SOCK_STREAM ) -let tcp6 = ( "tcp6", posix.AF_INET6, posix.SOCK_STREAM ) -let udp4 = ( "udp", posix.AF_INET, posix.SOCK_DGRAM ) -let udp6 = ( "udp6", posix.AF_INET6, posix.SOCK_DGRAM ) -let unix = ( "unix", posix.AF_UNIX, posix.SOCK_RAW ) # raw probably isn't right +let tcp4 = ("tcp", posix.AF_INET, posix.SOCK_STREAM) +let tcp6 = ("tcp6", posix.AF_INET6, posix.SOCK_STREAM) +let udp4 = ("udp", posix.AF_INET, posix.SOCK_DGRAM) +let udp6 = ("udp6", posix.AF_INET6, posix.SOCK_DGRAM) +let unix = ("unix", posix.AF_UNIX, posix.SOCK_RAW) # raw probably isn't right let tmap = { "all": @[tcp4, tcp6, udp4, udp6, unix], "tcp": @[tcp4, tcp6], - "tcp4": @[tcp4,], - "tcp6": @[tcp6,], + "tcp4": @[tcp4, ], + "tcp6": @[tcp6, ], "udp": @[udp4, udp6], - "udp4": @[udp4,], - "udp6": @[udp6,], - "unix": @[unix,], + "udp4": @[udp4, ], + "udp6": @[udp6, ], + "unix": @[unix, ], "inet": @[tcp4, tcp6, udp4, udp6], "inet4": @[tcp4, udp4], "inet6": @[tcp6, udp6], @@ -53,82 +53,82 @@ const TCP_STATUSES = { "0B": "CLOSING" }.toOrderedTable() -let CLOCK_TICKS = sysconf( SC_CLK_TCK ) -let PAGESIZE = sysconf( SC_PAGE_SIZE ) +let CLOCK_TICKS = sysconf(SC_CLK_TCK) +let PAGESIZE = sysconf(SC_PAGE_SIZE) proc get_sector_size(): int = - try: - return "/sys/block/sda/queue/hw_sector_size".readFile().parseInt() - except: - # man iostat states that sectors are equivalent with blocks and - # have a size of 512 bytes since 2.4 kernels. This value is - # needed to calculate the amount of disk I/O in bytes. - return 512 + try: + return "/sys/block/sda/queue/hw_sector_size".readFile().parseInt() + except: + # man iostat states that sectors are equivalent with blocks and + # have a size of 512 bytes since 2.4 kernels. This value is + # needed to calculate the amount of disk I/O in bytes. + return 512 let SECTOR_SIZE = get_sector_size() type timeval_32 = object - tv_sec: int32 # Seconds. - tv_usec: int32 # Microseconds. + tv_sec: int32 # Seconds. + tv_usec: int32 # Microseconds. type exit_status = object - e_termination: int16 # Process termination status. - e_exit: int16 # Process exit status. + e_termination: int16 # Process termination status. + e_exit: int16 # Process exit status. type utmp = object - ut_type: int16 # Type of login. - ut_pid: Pid # Process ID of login process. - ut_line: array[UT_LINESIZE, char] # Devicename. - ut_id: array[4, char] # Inittab ID. - ut_user: array[UT_NAMESIZE, char] # Username. - ut_host: array[UT_HOSTSIZE, char] # Hostname for remote login. - ut_exit: exit_status # Exit status of a process marked as DEAD_PROCESS. - ut_session: int32 # Session ID, used for windowing. - ut_tv: timeval_32 # Time entry was made. - ut_addr_v6: array[4, int32] # Internet address of remote host. - unused: array[20, char] # Reserved for future use. + ut_type: int16 # Type of login. + ut_pid: Pid # Process ID of login process. + ut_line: array[UT_LINESIZE, char] # Devicename. + ut_id: array[4, char] # Inittab ID. + ut_user: array[UT_NAMESIZE, char] # Username. + ut_host: array[UT_HOSTSIZE, char] # Hostname for remote login. + ut_exit: exit_status # Exit status of a process marked as DEAD_PROCESS. + ut_session: int32 # Session ID, used for windowing. + ut_tv: timeval_32 # Time entry was made. + ut_addr_v6: array[4, int32] # Internet address of remote host. + unused: array[20, char] # Reserved for future use. type SysInfo = object - uptime*: uint # Seconds since boot - loads*: array[3, uint] # 1, 5, and 15 minute load averages - totalram*: uint # Total usable main memory size - freeram*: uint # Available memory size - sharedram*: uint # Amount of shared memory - bufferram*: uint # Memory used by buffers - totalswap*: uint # Total swap space size - freeswap*: uint # swap space still available - procs*: uint16 # Number of current processes - totalhigh*: uint # Total high memory size - freehigh*: uint # Available high memory size - mem_unit*: uint # Memory unit size in bytes - f: array[20-2*sizeof(int)-sizeof(int32), char] #Padding to 64 bytes + uptime*: uint # Seconds since boot + loads*: array[3, uint] # 1, 5, and 15 minute load averages + totalram*: uint # Total usable main memory size + freeram*: uint # Available memory size + sharedram*: uint # Amount of shared memory + bufferram*: uint # Memory used by buffers + totalswap*: uint # Total swap space size + freeswap*: uint # swap space still available + procs*: uint16 # Number of current processes + totalhigh*: uint # Total high memory size + freehigh*: uint # Available high memory size + mem_unit*: uint # Memory unit size in bytes + f: array[20-2*sizeof(int)-sizeof(int32), char] #Padding to 64 bytes type mntent = ref object - mnt_fsname*:cstring # name of mounted filesystem - mnt_dir*:cstring # filesystem path prefix - mnt_type*:cstring # mount type (see mntent.h) - mnt_opts*:cstring # mount options (see mntent.h) - mnt_freq*:int # dump frequency in days - mnt_passno*:int # pass number on parallel fsck + mnt_fsname*: cstring # name of mounted filesystem + mnt_dir*: cstring # filesystem path prefix + mnt_type*: cstring # mount type (see mntent.h) + mnt_opts*: cstring # mount options (see mntent.h) + mnt_freq*: int # dump frequency in days + mnt_passno*: int # pass number on parallel fsck type ethtool_cmd = object - cmd*: uint32 - supported*: uint32 - advertising*: uint32 - speed*: uint16 - duplex*: uint8 - port*: uint8 - phy_address*: uint8 - transceiver*: uint8 - autoneg*: uint8 - mdio_support*: uint8 - maxtxpkt*: uint32 - maxrxpkt*: uint32 - speed_hi*: uint16 - eth_tp_mdix*: uint8 - eth_tp_mdix_ctrl*: uint8 - lp_advertising*: uint32 - reserved*: array[2, uint32] + cmd*: uint32 + supported*: uint32 + advertising*: uint32 + speed*: uint16 + duplex*: uint8 + port*: uint8 + phy_address*: uint8 + transceiver*: uint8 + autoneg*: uint8 + mdio_support*: uint8 + maxtxpkt*: uint32 + maxrxpkt*: uint32 + speed_hi*: uint16 + eth_tp_mdix*: uint8 + eth_tp_mdix_ctrl*: uint8 + lp_advertising*: uint32 + reserved*: array[2, uint32] ################################################################################ proc getutent(): ptr utmp {.header: "".} @@ -138,873 +138,885 @@ proc sysinfo(info: var SysInfo): cint {.header: "".} proc setmntent(filename: cstring, `type`: cstring): File {.header: "".} proc getmntent(stream: File): mntent {.header: "".} proc endmntent(streamp: File): int {.header: "".} -proc readlink(path: cstring, buf: array, bufsiz: int): int {.header: "".} +proc readlink(path: cstring, buf: array, + bufsiz: int): int {.header: "".} proc getpwuid(uid: int): ptr Passwd {.header: "".} proc boot_time*(): int = - ## Return the system boot time expressed in seconds since the epoch, Integer type. - let stat_path = PROCFS_PATH / "stat" - for line in stat_path.lines: - if line.strip.startswith("btime"): - return line.strip.split()[1].parseInt() + ## Return the system boot time expressed in seconds since the epoch, Integer type. + let stat_path = PROCFS_PATH / "stat" + for line in stat_path.lines: + if line.strip.startswith("btime"): + return line.strip.split()[1].parseInt() - raise newException(OSError, "line 'btime' not found in $1" % stat_path) + raise newException(OSError, "line 'btime' not found in $1" % stat_path) proc uptime*(): int = ## Return the system uptime expressed in seconds, Integer type. epochTime().int - boot_time() -proc isnumber(s : string): bool = - #[ +proc isnumber(s: string): bool = + #[ function for check if string is a number ]# - for c in s: - if isdigit(c) == false: - return false + for c in s: + if isdigit(c) == false: + return false - return true + return true proc pids*(): seq[int] = - ## Returns a list of PIDs currently running on the system. - let all_files = toSeq( walkDir(PROCFS_PATH, relative=true) ) - return mapIt( filterIt( all_files, isnumber(it.path) ), parseInt( it.path ) ) + ## Returns a list of PIDs currently running on the system. + let all_files = toSeq(walkDir(PROCFS_PATH, relative = true)) + return mapIt(filterIt(all_files, isnumber(it.path)), parseInt(it.path)) -proc pid_exists*( pid: int ): bool = - ## Check For the existence of a unix pid +proc pid_exists*(pid: int): bool = + ## Check For the existence of a unix pid - let exists = psutil_posix.pid_exists( pid ) - if not exists: return false + let exists = psutil_posix.pid_exists(pid) + if not exists: return false - try: - # Note: already checked that this is faster than using a regular expr. - # Also (a lot) faster than doing "return pid in pids()" - let status_path = PROCFS_PATH / $pid / "status" - for line in status_path.lines: - if line.startswith( "Tgid:" ): - let tgid = parseInt( line.split()[1] ) - return tgid == pid - - raise newException(OSError, "Tgid line not found in " & status_path) - except: - return pid in pids() + try: + # Note: already checked that this is faster than using a regular expr. + # Also (a lot) faster than doing "return pid in pids()" + let status_path = PROCFS_PATH / $pid / "status" + for line in status_path.lines: + if line.startswith("Tgid:"): + let tgid = parseInt(line.split()[1]) + return tgid == pid + + raise newException(OSError, "Tgid line not found in " & status_path) + except: + return pid in pids() proc pid_cmdline*(pid: int): string = - ## Function for getting the cmdline of a pid - ## this gets path of command and arguments - - let cmdline_path = PROCFS_PATH / $pid / "cmdline" - return cmdline_path.readFile() + ## Function for getting the cmdline of a pid + ## this gets path of command and arguments + + let cmdline_path = PROCFS_PATH / $pid / "cmdline" + return cmdline_path.readFile() proc pids_cmdline*(pids: seq[int]): seq[string] = - ## function for getting the cmdline of a sequence of pids - ## this gets path of command and arguments - var ret: seq[string] - for pid in pids: - ret.add(pid_cmdline(pid)) + ## function for getting the cmdline of a sequence of pids + ## this gets path of command and arguments + var ret: seq[string] + for pid in pids: + ret.add(pid_cmdline(pid)) proc pid_name*(pid: int): string = - ## Function for getting the process name of a pid - ## not to be mixed with pid_cmdline. This only gets the - ## program name. Not the path and arguments - let p_path = PROCFS_PATH / $pid / "status" - var data = p_path.readFile() - for line in data.split("\n"): - if "Name:" in line: - var name = line.split("Name:")[1].strip() - result = name + ## Function for getting the process name of a pid + ## not to be mixed with pid_cmdline. This only gets the + ## program name. Not the path and arguments + let p_path = PROCFS_PATH / $pid / "status" + var data = p_path.readFile() + for line in data.split("\n"): + if "Name:" in line: + var name = line.split("Name:")[1].strip() + result = name proc pid_names*(pids: seq[int]): seq[string] = - ## Function for getting the process name of a sequence of pids - ## not to be mmixed with pids_cmdline. This only gets the - ## program name. Not the path and arguments. - var ret: seq[string] - for pid in pids: - ret.add(pid_name(pid)) - - return ret - -proc pid_path*(pid: int): string = - - ## Function for getting the path of the elf of the running pid - var p_path: cstring = PROCFS_PATH / $pid / "exe" - var buf: array[PATH_MAX, char] - if readlink(p_path, buf, PATH_MAX) == -1: - raise newException(IOError, "Cannot read /proc/$1/exe | $2" % [$pid, $strerror(errno)]) - for c in buf: - if c != '\0': result.add(c) else: break + ## Function for getting the process name of a sequence of pids + ## not to be mmixed with pids_cmdline. This only gets the + ## program name. Not the path and arguments. + var ret: seq[string] + for pid in pids: + ret.add(pid_name(pid)) + + return ret + +proc pid_path*(pid: int): string = + + ## Function for getting the path of the elf of the running pid + var p_path: cstring = PROCFS_PATH / $pid / "exe" + var buf: array[PATH_MAX, char] + if readlink(p_path, buf, PATH_MAX) == -1: + raise newException(IOError, "Cannot read /proc/$1/exe | $2" % [$pid, + $strerror(errno)]) + for c in buf: + if c != '\0': result.add(c) else: break proc try_pid_path*(pid: int): string = - ## Function for getting the path of the elf of the running pid - ## Note: Instead of raising an error. It will instread return "" - var p_path: cstring = PROCFS_PATH / $pid / "exe" - var buf: array[PATH_MAX, char] - if readlink(p_path, buf, PATH_MAX) == -1: - result = "" - else: - for c in buf: - if c != '\0': result.add(c) else: break + ## Function for getting the path of the elf of the running pid + ## Note: Instead of raising an error. It will instread return "" + var p_path: cstring = PROCFS_PATH / $pid / "exe" + var buf: array[PATH_MAX, char] + if readlink(p_path, buf, PATH_MAX) == -1: + result = "" + else: + for c in buf: + if c != '\0': result.add(c) else: break proc pid_paths*(pids: seq[int]): seq[string] = - ## Function for getting the elf paths of the specified pids - for pid in pids: - result.add(pid_path(pid)) + ## Function for getting the elf paths of the specified pids + for pid in pids: + result.add(pid_path(pid)) proc try_pid_paths*(pids: seq[int]): seq[string] = - - ## Function for getting the paths of the specified pids - ## Note: If an error occurs for any of the pids. The result for the corresponding - ## pid will be "" - for pid in pids: - result.add(try_pid_path(pid)) - + + ## Function for getting the paths of the specified pids + ## Note: If an error occurs for any of the pids. The result for the corresponding + ## pid will be "" + for pid in pids: + result.add(try_pid_path(pid)) + proc pid_user*(pid: int): string = - - ## Function for getting the username running the specified pid - var p_path = PROCFS_PATH / $pid / "status" - var uid = -1 - var data = p_path.readFile() - for line in data.split("\n"): - if "Uid:" in line: - uid = parseInt(line.split("Uid:")[1].strip().split("\t")[0]) - - var pws = getpwuid(cast[Uid](uid)) - if pws.isNil: - raise newException(OSError, "UID $1 not found" % [$uid]) - result = $pws.pw_name + + ## Function for getting the username running the specified pid + var p_path = PROCFS_PATH / $pid / "status" + var uid = -1 + var data = p_path.readFile() + for line in data.split("\n"): + if "Uid:" in line: + uid = parseInt(line.split("Uid:")[1].strip().split("\t")[0]) + + var pws = getpwuid(cast[Uid](uid)) + if pws.isNil: + raise newException(OSError, "UID $1 not found" % [$uid]) + result = $pws.pw_name proc try_pid_user*(pid: int): string = - - ## Function for getting the username running the specified pid - var p_path = PROCFS_PATH / $pid / "status" - var uid = -1 - var data = p_path.readFile() - for line in data.split("\n"): - if "Uid:" in line: - uid = parseInt(line.split("Uid:")[1].strip().split("\t")[0]) - - var pws = getpwuid(cast[Uid](uid)) - if pws.isNil: - result = "" - else: - result = $pws.pw_name + + ## Function for getting the username running the specified pid + var p_path = PROCFS_PATH / $pid / "status" + var uid = -1 + var data = p_path.readFile() + for line in data.split("\n"): + if "Uid:" in line: + uid = parseInt(line.split("Uid:")[1].strip().split("\t")[0]) + + var pws = getpwuid(cast[Uid](uid)) + if pws.isNil: + result = "" + else: + result = $pws.pw_name proc pid_users*(pids: seq[int]): seq[string] = - for pid in pids: - result.add(pid_user(pid)) + for pid in pids: + result.add(pid_user(pid)) proc try_pid_users*(pids: seq[int]): seq[string] = - for pid in pids: - result.add(try_pid_user(pid)) + for pid in pids: + result.add(try_pid_user(pid)) proc pid_parent*(pid: int): int = - - ## Function for getting the parent pid of the specified pid - var p_path = PROCFS_PATH / $pid / "status" - var data = p_path.readFile() - for line in data.split("\n"): - if "PPid:" in line: - result = parseInt(line.split("PPid:")[^1].strip()) + + ## Function for getting the parent pid of the specified pid + var p_path = PROCFS_PATH / $pid / "status" + var data = p_path.readFile() + for line in data.split("\n"): + if "PPid:" in line: + result = parseInt(line.split("PPid:")[^1].strip()) proc pid_parents*(pids: seq[int]): seq[int] = - ## Function for getting the parent pids of the corresponding pids specified. - for pid in pids: - result.add(pid_parent(pid)) + ## Function for getting the parent pids of the corresponding pids specified. + for pid in pids: + result.add(pid_parent(pid)) proc process_exists*(processName: string): bool = - let names_seq = pid_names(pids()) - for name in names_seq: - if processName == name: - return true + let names_seq = pid_names(pids()) + for name in names_seq: + if processName == name: + return true - return false + return false proc pids_with_names*(): (seq[int], seq[string]) = - ## Function for returning tuple of pids and names - - var pids_seq = pids() - var names_seq = pid_names(pids_seq) + ## Function for returning tuple of pids and names - return (pids_seq, names_seq) + var pids_seq = pids() + var names_seq = pid_names(pids_seq) + + return (pids_seq, names_seq) proc users*(): seq[User] = - result = newSeq[User]() + result = newSeq[User]() - setutent() + setutent() - var ut = getutent() - while ut != nil: - let is_user_proc = ut.ut_type == USER_PROCESS - if not is_user_proc: - ut = getutent() - continue + var ut = getutent() + while ut != nil: + let is_user_proc = ut.ut_type == USER_PROCESS + if not is_user_proc: + ut = getutent() + continue - var hostname = $ut.ut_host - if hostname == ":0.0" or hostname == ":0": - hostname = "localhost" + var hostname = $ut.ut_host + if hostname == ":0.0" or hostname == ":0": + hostname = "localhost" - let user_tuple = User( name:($ut.ut_user.join().strip.replace("\x00", "")), - terminal:($ut.ut_line.join().strip.replace("\x00", "")), - started:ut.ut_tv.tv_sec.float ) - result.add( user_tuple ) - ut = getutent() + let user_tuple = User(name: ($ut.ut_user.join().strip.replace("\x00", "")), + terminal: ($ut.ut_line.join().strip.replace("\x00", + "")), + started: ut.ut_tv.tv_sec.float) + result.add(user_tuple) + ut = getutent() - endutent() + endutent() proc parse_cpu_time_line(text: string): CPUTimes = - let values = text.strip.splitWhitespace() - let times = mapIt(values[1..len(values) - 1], parseFloat(it) / CLOCK_TICKS.float) - if len(times) >= 7: - result.user = parseFloat(fmt"{times[0]:.2f}") - result.nice = parseFloat(fmt"{times[1]:.2f}") - result.system = parseFloat(fmt"{times[2]:.2f}") - result.idle = parseFloat(fmt"{times[3]:.2f}") - result.iowait = parseFloat(fmt"{times[4]:.2f}") - result.irq = parseFloat(fmt"{times[5]:.2f}") - result.softirq = parseFloat(fmt"{times[6]:.2f}") - if len(times) >= 8: - result.steal = parseFloat(fmt"{times[7]:.2f}") - if len(times) >= 9: - result.guest = parseFloat(fmt"{times[8]:.2f}") - if len(times) >= 10: - result.guest_nice = parseFloat(fmt"{times[9]:.2f}") + let values = text.strip.splitWhitespace() + let times = mapIt(values[1..len(values) - 1], parseFloat(it) / + CLOCK_TICKS.float) + if len(times) >= 7: + result.user = parseFloat(fmt"{times[0]:.2f}") + result.nice = parseFloat(fmt"{times[1]:.2f}") + result.system = parseFloat(fmt"{times[2]:.2f}") + result.idle = parseFloat(fmt"{times[3]:.2f}") + result.iowait = parseFloat(fmt"{times[4]:.2f}") + result.irq = parseFloat(fmt"{times[5]:.2f}") + result.softirq = parseFloat(fmt"{times[6]:.2f}") + if len(times) >= 8: + result.steal = parseFloat(fmt"{times[7]:.2f}") + if len(times) >= 9: + result.guest = parseFloat(fmt"{times[8]:.2f}") + if len(times) >= 10: + result.guest_nice = parseFloat(fmt"{times[9]:.2f}") proc cpu_times*(): CPUTimes = - # Return a tuple representing the following system-wide CPU times: - # (user, nice, system, idle, iowait, irq, softirq [steal, [guest, - # [guest_nice]]]) - # Last 3 fields may not be available on all Linux kernel versions. - for line in lines( PROCFS_PATH / "stat" ): - result = parse_cpu_time_line(line) - break + # Return a tuple representing the following system-wide CPU times: + # (user, nice, system, idle, iowait, irq, softirq [steal, [guest, + # [guest_nice]]]) + # Last 3 fields may not be available on all Linux kernel versions. + for line in lines(PROCFS_PATH / "stat"): + result = parse_cpu_time_line(line) + break proc per_cpu_times*(): seq[CPUTimes] = - ## Return a list of tuples representing the CPU times for every - ## CPU available on the system. - result = newSeq[CPUTimes]() - for line in lines( PROCFS_PATH / "stat" ): - if not line.startswith("cpu"): continue - let entry = parse_cpu_time_line( line ) - result.add( entry ) - # get rid of the first line which refers to system wide CPU stats - result.delete(0) - return result - - -proc cpu_stats*(): tuple[ctx_switches, interrupts, soft_interrupts, syscalls: int] = - var ctx_switches, interrupts, soft_interrupts = 0 - for line in lines( PROCFS_PATH / "stat" ): - if line.startswith("ctxt"): - ctx_switches = parseint( line.split()[1] ) - elif line.startswith("intr"): - interrupts = parseint( line.split()[1] ) - elif line.startswith("softirq"): - soft_interrupts = parseint( line.split()[1] ) - if ctx_switches != 0 and soft_interrupts != 0 and interrupts != 0: - break - # syscalls = 0 - return (ctx_switches, interrupts, soft_interrupts, 0) + ## Return a list of tuples representing the CPU times for every + ## CPU available on the system. + result = newSeq[CPUTimes]() + for line in lines(PROCFS_PATH / "stat"): + if not line.startswith("cpu"): continue + let entry = parse_cpu_time_line(line) + result.add(entry) + # get rid of the first line which refers to system wide CPU stats + result.delete(0) + return result + + +proc cpu_stats*(): tuple[ctx_switches, interrupts, soft_interrupts, + syscalls: int] = + var ctx_switches, interrupts, soft_interrupts = 0 + for line in lines(PROCFS_PATH / "stat"): + if line.startswith("ctxt"): + ctx_switches = parseint(line.split()[1]) + elif line.startswith("intr"): + interrupts = parseint(line.split()[1]) + elif line.startswith("softirq"): + soft_interrupts = parseint(line.split()[1]) + if ctx_switches != 0 and soft_interrupts != 0 and interrupts != 0: + break + # syscalls = 0 + return (ctx_switches, interrupts, soft_interrupts, 0) proc cpu_count_logical*(): int = - ## Return the number of logical CPUs in the system. - try: - return sysconf( SC_NPROCESSORS_ONLN ) - except ValueError: - # as a second fallback we try to parse /proc/cpuinfo - for line in lines(PROCFS_PATH / "cpuinfo"): - if line.toLowerAscii().startswith("processor"): - result += 1 + ## Return the number of logical CPUs in the system. + try: + return sysconf(SC_NPROCESSORS_ONLN) + except ValueError: + # as a second fallback we try to parse /proc/cpuinfo + for line in lines(PROCFS_PATH / "cpuinfo"): + if line.toLowerAscii().startswith("processor"): + result += 1 + + # unknown format (e.g. amrel/sparc architectures), see: + # https://github.com/giampaolo/psutil/issues/200 + # try to parse /proc/stat as a last resort + if result == 0: + for line in lines(PROCFS_PATH / "stat"): + if line.toLowerAscii().startswith("cpu"): + result += 1 + # Remove one from the count for the top "cpu" line (with no digit) + # Saves us the regular expression used in the python impl + if result != 0: result -= 1 - # unknown format (e.g. amrel/sparc architectures), see: - # https://github.com/giampaolo/psutil/issues/200 - # try to parse /proc/stat as a last resort - if result == 0: - for line in lines(PROCFS_PATH / "stat"): - if line.toLowerAscii().startswith("cpu"): - result += 1 - # Remove one from the count for the top "cpu" line (with no digit) - # Saves us the regular expression used in the python impl - if result != 0: result -= 1 - - return result + return result proc cpu_count_physical*(): int = - ## Return the number of physical cores in the system. - var mapping = initTable[int, int]() - var current_info = initTable[string, int]() - for raw_line in lines(PROCFS_PATH / "cpuinfo"): - let line = raw_line.strip().toLowerAscii() - if line == "": - # new section - if "physical id" in current_info and "cpu cores" in current_info: - mapping[current_info["physical id"]] = current_info["cpu cores"] - current_info = initTable[string, int]() - else: - # ongoing section - if line.startswith("physical id") or line.startswith("cpu cores"): - let parts = line.split("\t:") - current_info[parts[0].strip()] = parseInt(parts[1].strip()) - - let values = toSeq(mapping.values()) - return sum(values) - - -proc calculate_avail_vmem( mems:TableRef[string,int] ): int = - ## Fallback for kernels < 3.14 where /proc/meminfo does not provide - ## "MemAvailable:" column (see: https://blog.famzah.net/2014/09/24/). - ## This code reimplements the algorithm outlined here: - ## https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/ - ## commit/?id=34e431b0ae398fc54ea69ff85ec700722c9da773 - ## XXX: on recent kernels this calculation differs by ~1.5% than - ## "MemAvailable:" as it's calculated slightly differently, see: - ## https://gitlab.com/procps-ng/procps/issues/42 - ## https://github.com/famzah/linux-memavailable-procfs/issues/2 - ## It is still way more realistic than doing (free + cached) though. - - # Fallback for very old distros. According to - # https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/ - # commit/?id=34e431b0ae398fc54ea69ff85ec700722c9da773 - # ...long ago "avail" was calculated as (free + cached). - # We might fallback in such cases: - # "Active(file)" not available: 2.6.28 / Dec 2008 - # "Inactive(file)" not available: 2.6.28 / Dec 2008 - # "SReclaimable:" not available: 2.6.19 / Nov 2006 - # /proc/zoneinfo not available: 2.6.13 / Aug 2005 - let free = mems["MemFree:"] - let fallback = free + mems.getOrDefault("Cached:") - - var lru_active_file = 0 - var lru_inactive_file = 0 - var slab_reclaimable = 0 - try: - lru_active_file = mems["Active(file):"] - lru_inactive_file = mems["Inactive(file):"] - slab_reclaimable = mems["SReclaimable:"] - except KeyError: - return fallback - - var watermark_low = 0 - try: - for line in lines( PROCFS_PATH / "zoneinfo" ): - if line.strip().startswith("low"): - watermark_low += parseInt(line.splitWhitespace()[1]) - except IOError: - return fallback # kernel 2.6.13 - - watermark_low *= PAGESIZE - watermark_low = watermark_low - - var avail = free - watermark_low - var pagecache = lru_active_file + lru_inactive_file - pagecache -= min(int(pagecache / 2), watermark_low) - avail += pagecache - avail += slab_reclaimable - min(int(slab_reclaimable / 2), watermark_low) - return int(avail) + ## Return the number of physical cores in the system. + var mapping = initTable[int, int]() + var current_info = initTable[string, int]() + for raw_line in lines(PROCFS_PATH / "cpuinfo"): + let line = raw_line.strip().toLowerAscii() + if line == "": + # new section + if "physical id" in current_info and "cpu cores" in current_info: + mapping[current_info["physical id"]] = current_info["cpu cores"] + current_info = initTable[string, int]() + else: + # ongoing section + if line.startswith("physical id") or line.startswith("cpu cores"): + let parts = line.split("\t:") + current_info[parts[0].strip()] = parseInt(parts[1].strip()) + + let values = toSeq(mapping.values()) + return sum(values) + + +proc calculate_avail_vmem(mems: TableRef[string, int]): int = + ## Fallback for kernels < 3.14 where /proc/meminfo does not provide + ## "MemAvailable:" column (see: https://blog.famzah.net/2014/09/24/). + ## This code reimplements the algorithm outlined here: + ## https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/ + ## commit/?id=34e431b0ae398fc54ea69ff85ec700722c9da773 + ## XXX: on recent kernels this calculation differs by ~1.5% than + ## "MemAvailable:" as it's calculated slightly differently, see: + ## https://gitlab.com/procps-ng/procps/issues/42 + ## https://github.com/famzah/linux-memavailable-procfs/issues/2 + ## It is still way more realistic than doing (free + cached) though. + + # Fallback for very old distros. According to + # https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/ + # commit/?id=34e431b0ae398fc54ea69ff85ec700722c9da773 + # ...long ago "avail" was calculated as (free + cached). + # We might fallback in such cases: + # "Active(file)" not available: 2.6.28 / Dec 2008 + # "Inactive(file)" not available: 2.6.28 / Dec 2008 + # "SReclaimable:" not available: 2.6.19 / Nov 2006 + # /proc/zoneinfo not available: 2.6.13 / Aug 2005 + let free = mems["MemFree:"] + let fallback = free + mems.getOrDefault("Cached:") + + var lru_active_file = 0 + var lru_inactive_file = 0 + var slab_reclaimable = 0 + try: + lru_active_file = mems["Active(file):"] + lru_inactive_file = mems["Inactive(file):"] + slab_reclaimable = mems["SReclaimable:"] + except KeyError: + return fallback + + var watermark_low = 0 + try: + for line in lines(PROCFS_PATH / "zoneinfo"): + if line.strip().startswith("low"): + watermark_low += parseInt(line.splitWhitespace()[1]) + except IOError: + return fallback # kernel 2.6.13 + + watermark_low *= PAGESIZE + watermark_low = watermark_low + + var avail = free - watermark_low + var pagecache = lru_active_file + lru_inactive_file + pagecache -= min(int(pagecache / 2), watermark_low) + avail += pagecache + avail += slab_reclaimable - min(int(slab_reclaimable / 2), watermark_low) + return int(avail) proc virtual_memory*(): VirtualMemory = - ## Report virtual memory stats. - ## This implementation matches "free" and "vmstat -s" cmdline - ## utility values and procps-ng-3.3.12 source was used as a reference - ## (2016-09-18): - ## https://gitlab.com/procps-ng/procps/blob/ - ## 24fd2605c51fccc375ab0287cec33aa767f06718/proc/sysinfo.c - ## For reference, procps-ng-3.3.10 is the version available on Ubuntu - ## 16.04. - ## Note about "available" memory: up until psutil 4.3 it was - ## calculated as "avail = (free + buffers + cached)". Now - ## "MemAvailable:" column (kernel 3.14) from /proc/meminfo is used as - ## it's more accurate. - ## That matches "available" column in newer versions of "free". - - var missing_fields = newSeq[string]() - var mems = newTable[string, int]() - for line in lines( PROCFS_PATH / "meminfo" ): - let fields = line.splitWhitespace() - mems[fields[0]] = parseInt(fields[1]) * 1024 - - # /proc doc states that the available fields in /proc/meminfo vary - # by architecture and compile options, but these 3 values are also - # returned by sysinfo(2); as such we assume they are always there. - let total = mems["MemTotal:"] - let free = mems["MemFree:"] - let buffers = mems["Buffers:"] - - var cached = 0 + ## Report virtual memory stats. + ## This implementation matches "free" and "vmstat -s" cmdline + ## utility values and procps-ng-3.3.12 source was used as a reference + ## (2016-09-18): + ## https://gitlab.com/procps-ng/procps/blob/ + ## 24fd2605c51fccc375ab0287cec33aa767f06718/proc/sysinfo.c + ## For reference, procps-ng-3.3.10 is the version available on Ubuntu + ## 16.04. + ## Note about "available" memory: up until psutil 4.3 it was + ## calculated as "avail = (free + buffers + cached)". Now + ## "MemAvailable:" column (kernel 3.14) from /proc/meminfo is used as + ## it's more accurate. + ## That matches "available" column in newer versions of "free". + + var missing_fields = newSeq[string]() + var mems = newTable[string, int]() + for line in lines(PROCFS_PATH / "meminfo"): + let fields = line.splitWhitespace() + mems[fields[0]] = parseInt(fields[1]) * 1024 + + # /proc doc states that the available fields in /proc/meminfo vary + # by architecture and compile options, but these 3 values are also + # returned by sysinfo(2); as such we assume they are always there. + let total = mems["MemTotal:"] + let free = mems["MemFree:"] + let buffers = mems["Buffers:"] + + var cached = 0 + try: + cached = mems["Cached:"] + # "free" cmdline utility sums reclaimable to cached. + # Older versions of procps used to add slab memory instead. + # This got changed in: + # https://gitlab.com/procps-ng/procps/commit/ + # 05d751c4f076a2f0118b914c5e51cfbb4762ad8e + cached += mems.getOrDefault("SReclaimable:") # since kernel 2.6.19 + except KeyError: + missing_fields.add("cached") + + var shared = 0 + try: + shared = mems["Shmem:"] # since kernel 2.6.32 + except KeyError: try: - cached = mems["Cached:"] - # "free" cmdline utility sums reclaimable to cached. - # Older versions of procps used to add slab memory instead. - # This got changed in: - # https://gitlab.com/procps-ng/procps/commit/ - # 05d751c4f076a2f0118b914c5e51cfbb4762ad8e - cached += mems.getOrDefault("SReclaimable:") # since kernel 2.6.19 + shared = mems["MemShared:"] # kernels 2.4 except KeyError: - missing_fields.add("cached") - - var shared = 0 + missing_fields.add("shared") + + var active = 0 + try: + active = mems["Active:"] + except KeyError: + missing_fields.add("active") + + var inactive = 0 + try: + inactive = mems["Inactive:"] + except KeyError: try: - shared = mems["Shmem:"] # since kernel 2.6.32 + inactive = mems["Inact_dirty:"] + mems["Inact_clean:"] + mems["Inact_laundry:"] except KeyError: - try: - shared = mems["MemShared:"] # kernels 2.4 - except KeyError: - missing_fields.add("shared") + missing_fields.add("inactive") - var active = 0 - try: - active = mems["Active:"] - except KeyError: - missing_fields.add("active") - - var inactive = 0 - try: - inactive = mems["Inactive:"] - except KeyError: - try: - inactive = mems["Inact_dirty:"] + mems["Inact_clean:"] + mems["Inact_laundry:"] - except KeyError: - missing_fields.add("inactive") - - var used = total - free - cached - buffers - if used < 0: - # May be symptomatic of running within a LCX container where such - # values will be dramatically distorted over those of the host. - used = total - free - - # - starting from 4.4.0 we match free's "available" column. - # Before 4.4.0 we calculated it as (free + buffers + cached) - # which matched htop. - # - free and htop available memory differs as per: - # http://askubuntu.com/a/369589 - # http://unix.stackexchange.com/a/65852/168884 - # - MemAvailable has been introduced in kernel 3.14 - var avail = 0 - try: - avail = mems["MemAvailable:"] - except KeyError: - avail = calculate_avail_vmem(mems) + var used = total - free - cached - buffers + if used < 0: + # May be symptomatic of running within a LCX container where such + # values will be dramatically distorted over those of the host. + used = total - free + + # - starting from 4.4.0 we match free's "available" column. + # Before 4.4.0 we calculated it as (free + buffers + cached) + # which matched htop. + # - free and htop available memory differs as per: + # http://askubuntu.com/a/369589 + # http://unix.stackexchange.com/a/65852/168884 + # - MemAvailable has been introduced in kernel 3.14 + var avail = 0 + try: + avail = mems["MemAvailable:"] + except KeyError: + avail = calculate_avail_vmem(mems) + + if avail < 0: + avail = 0 + missing_fields.add("available") + + # If avail is greater than total or our calculation overflows, + # that's symptomatic of running within a LCX container where such + # values will be dramatically distorted over those of the host. + # https://gitlab.com/procps-ng/procps/blob/ + # 24fd2605c51fccc375ab0287cec33aa767f06718/proc/sysinfo.c#L764 + if avail > total: + avail = free + + let percent = usage_percent( (total - avail), total, places = 1) + + # Warn about missing metrics which are set to 0. + if len(missing_fields) > 0: + echo(missing_fields.join(", "), + " memory stats couldn't be determined and ", + if len(missing_fields) == 1: "was" else: "were", + " set to 0") + + return VirtualMemory(total: total, avail: avail, percent: percent, used: used, + free: free, active: active, inactive: inactive, + buffers: buffers, cached: cached, shared: shared) - if avail < 0: - avail = 0 - missing_fields.add("available") - # If avail is greater than total or our calculation overflows, - # that's symptomatic of running within a LCX container where such - # values will be dramatically distorted over those of the host. - # https://gitlab.com/procps-ng/procps/blob/ - # 24fd2605c51fccc375ab0287cec33aa767f06718/proc/sysinfo.c#L764 - if avail > total: - avail = free +proc swap_memory*(): SwapMemory = + var si: SysInfo + if sysinfo(si) == -1: + echo("Error calling sysinfo in swap_memory(): ", errno) + return + + let total = si.totalswap * si.mem_unit + let free = si.freeswap * si.mem_unit + let used = total - free + let percent = usage_percent(used.int, total.int, places = 1) + + result = SwapMemory(total: total.int, used: used.int, free: free.int, + percent: percent, sin: 0, sout: 0) + + # try to get pgin/pgouts + if not existsFile(PROCFS_PATH / "vmstat"): + # see https://github.com/giampaolo/psutil/issues/722 + echo("'sin' and 'sout' swap memory stats couldn't be determined ", + "and were set to 0") + return result - let percent = usage_percent( (total - avail), total, places=1 ) + for line in lines(PROCFS_PATH / "vmstat"): + # values are expressed in 4 kilo bytes, we want bytes instead + if line.startswith("pswpin"): + result.sin = parseInt(line.split()[1]) * 4 * 1024 + elif line.startswith("pswpout"): + result.sout = parseInt(line.split()[1]) * 4 * 1024 + if result.sin != 0 and result.sout != 0: + return result + + # we might get here when dealing with exotic Linux flavors, see: + # https://github.com/giampaolo/psutil/issues/313 + echo("'sin' and 'sout' swap memory stats couldn't be determined ", + "and were set to 0") + + +proc disk_partitions*(all = false): seq[DiskPartition] = + ## Return mounted disk partitions as a sequence of DiskPartitions + var fstypes = initHashSet[string]() + for raw_line in lines(PROCFS_PATH / "filesystems"): + let line = raw_line.strip() + if not line.startswith("nodev"): + fstypes.incl(line) + else: + # ignore all lines starting with "nodev" except "nodev zfs" + if line.split("\t")[1] == "zfs": + fstypes.incl("zfs") - # Warn about missing metrics which are set to 0. - if len( missing_fields ) > 0: - echo( missing_fields.join( ", " ), - " memory stats couldn't be determined and ", - if len(missing_fields) == 1: "was" else: "were", - " set to 0" ) + result = newSeq[DiskPartition]() - return VirtualMemory( total:total, avail:avail, percent:percent, used:used, - free:free, active:active, inactive:inactive, - buffers:buffers, cached:cached, shared:shared ) + let file = setmntent(MOUNTED, "r"); + var entry = getmntent(file) + while entry != nil: + let device = if entry.mnt_fsname == "none": "" else: $entry.mnt_fsname + let mountpoint = $entry.mnt_dir + let fstype = $entry.mnt_type + let opts = $entry.mnt_opts + if not all: + if device == "" or not(fstype in fstypes): + entry = getmntent(file) + continue + let partition = DiskPartition(device: device, mountpoint: mountpoint, + fstype: fstype, opts: opts) + result.add(partition) + entry = getmntent(file) -proc swap_memory*(): SwapMemory = - var si: SysInfo - if sysinfo( si ) == -1: - echo( "Error calling sysinfo in swap_memory(): ", errno ) - return - - let total = si.totalswap * si.mem_unit - let free = si.freeswap * si.mem_unit - let used = total - free - let percent = usage_percent(used.int, total.int, places=1) - - result = SwapMemory( total:total.int, used:used.int, free:free.int, - percent:percent, sin:0, sout:0 ) - - # try to get pgin/pgouts - if not existsFile( PROCFS_PATH / "vmstat" ): - # see https://github.com/giampaolo/psutil/issues/722 - echo( "'sin' and 'sout' swap memory stats couldn't be determined ", - "and were set to 0" ) - return result - - for line in lines( PROCFS_PATH / "vmstat" ): - # values are expressed in 4 kilo bytes, we want bytes instead - if line.startswith("pswpin"): - result.sin = parseInt(line.split()[1]) * 4 * 1024 - elif line.startswith("pswpout"): - result.sout = parseInt(line.split()[1]) * 4 * 1024 - if result.sin != 0 and result.sout != 0: - return result - - # we might get here when dealing with exotic Linux flavors, see: - # https://github.com/giampaolo/psutil/issues/313 - echo( "'sin' and 'sout' swap memory stats couldn't be determined ", - "and were set to 0" ) - - -proc disk_partitions*(all=false): seq[DiskPartition] = - ## Return mounted disk partitions as a sequence of DiskPartitions - var fstypes = initHashSet[string]() - for raw_line in lines( PROCFS_PATH / "filesystems" ): - let line = raw_line.strip() - if not line.startswith("nodev"): - fstypes.incl( line ) - else: - # ignore all lines starting with "nodev" except "nodev zfs" - if line.split("\t")[1] == "zfs": - fstypes.incl( "zfs" ) - - result = newSeq[DiskPartition]() - - let file = setmntent(MOUNTED, "r"); - var entry = getmntent( file ) - while entry != nil: - let device = if entry.mnt_fsname == "none": "" else: $entry.mnt_fsname - let mountpoint = $entry.mnt_dir - let fstype = $entry.mnt_type - let opts = $entry.mnt_opts - - if not all: - if device == "" or not( fstype in fstypes ): - entry = getmntent( file ) - continue - let partition = DiskPartition( device:device, mountpoint:mountpoint, - fstype:fstype, opts:opts ) - result.add( partition ) - entry = getmntent( file ) - - discard endmntent( file ) + discard endmntent(file) proc per_nic_net_io_counters*(): TableRef[string, NetIO] = - ## Return network I/O statistics for every network interface - ## installed on the system as a dict of raw tuples. - result = newTable[string, NetIO]() - for line in lines( PROCFS_PATH / "net/dev" ): - if not( ":" in line ): continue - let colon = line.rfind(':') - let name = line[..colon].strip() - let lst = line[(colon + 1)..len(line) - 1].strip.replace("\x00", "").splitWhitespace - let fields = mapIt(lst, parseInt(it)) - - result[name] = NetIO( bytes_sent: fields[8], - bytes_recv: fields[0], - packets_sent: fields[9], - packets_recv: fields[1], - errin: fields[2], - errout: fields[10], - dropin: fields[3], - dropout: fields[11] ) - - -proc net_if_duplex_speed*( name: string ): tuple[ duplex: NicDuplex, speed: int ] = - ## Return stats about a particular network interface. - ## References: - ## https://github.com/dpaleino/wicd/blob/master/wicd/backends/be-ioctl.py - ## http://www.i-scream.org/libstatgrab/ - - result = ( NIC_DUPLEX_UNKNOWN, 0 ) - - var ifr: ifreq - var ethcmd: ethtool_cmd - ethcmd.cmd = ETHTOOL_GSET - ifr.ifr_ifru.ifru_data = addr ethcmd - if not ioctlsocket( name, SIOCETHTOOL, ifr ): - return result - - let duplex_map = { DUPLEX_FULL: NIC_DUPLEX_FULL, - DUPLEX_HALF: NIC_DUPLEX_HALF, - DUPLEX_UNKNOWN: NIC_DUPLEX_UNKNOWN }.toTable() - result.duplex = duplex_map[ethcmd.duplex] - result.speed = int( ethcmd.speed ) + ## Return network I/O statistics for every network interface + ## installed on the system as a dict of raw tuples. + result = newTable[string, NetIO]() + for line in lines(PROCFS_PATH / "net/dev"): + if not(":" in line): continue + let colon = line.rfind(':') + let name = line[..colon].strip() + let lst = line[(colon + 1)..len(line) - 1].strip.replace("\x00", + "").splitWhitespace + let fields = mapIt(lst, parseInt(it)) + + result[name] = NetIO(bytes_sent: fields[8], + bytes_recv: fields[0], + packets_sent: fields[9], + packets_recv: fields[1], + errin: fields[2], + errout: fields[10], + dropin: fields[3], + dropout: fields[11]) + + +proc net_if_duplex_speed*(name: string): tuple[duplex: NicDuplex, speed: int] = + ## Return stats about a particular network interface. + ## References: + ## https://github.com/dpaleino/wicd/blob/master/wicd/backends/be-ioctl.py + ## http://www.i-scream.org/libstatgrab/ + + result = (NIC_DUPLEX_UNKNOWN, 0) + + var ifr: ifreq + var ethcmd: ethtool_cmd + ethcmd.cmd = ETHTOOL_GSET + ifr.ifr_ifru.ifru_data = addr ethcmd + if not ioctlsocket(name, SIOCETHTOOL, ifr): + return result + + let duplex_map = {DUPLEX_FULL: NIC_DUPLEX_FULL, + DUPLEX_HALF: NIC_DUPLEX_HALF, + DUPLEX_UNKNOWN: NIC_DUPLEX_UNKNOWN}.toTable() + result.duplex = duplex_map[ethcmd.duplex] + result.speed = int(ethcmd.speed) proc net_if_stats*(): TableRef[string, NICstats] = - ## Get NIC stats (isup, duplex, speed, mtu). - let names = toSeq( per_nic_net_io_counters().keys() ) - result = newTable[string, NICStats]() - for name in names: - let (duplex, speed) = net_if_duplex_speed( name ) - result[name] = NICStats( isup:net_if_flags( name ), - duplex:duplex, - speed:speed, - mtu:net_if_mtu( name ) ) + ## Get NIC stats (isup, duplex, speed, mtu). + let names = toSeq(per_nic_net_io_counters().keys()) + result = newTable[string, NICStats]() + for name in names: + let (duplex, speed) = net_if_duplex_speed(name) + result[name] = NICStats(isup: net_if_flags(name), + duplex: duplex, + speed: speed, + mtu: net_if_mtu(name)) proc get_partitions*(): seq[string] = - # Determine partitions to look for - result = newSeq[string]() - var lines = toSeq( lines( PROCFS_PATH / "partitions" ) ) - for line in reversed( lines[2.. ( "10.0.0.5", 22 ) + ## "0000000000000000FFFF00000100007F:9E49" -> ( "::ffff:127.0.0.1", 40521 ) + ## The IP address portion is a little or big endian four-byte + ## hexadecimal number; that is, the least significant byte is listed + ## first, so we need to reverse the order of the bytes to convert it + ## to an IP address. + ## The port is represented as a two-byte hexadecimal number. + ## Reference: + ## http://linuxdevcenter.com/pub/a/linux/2000/11/16/LinuxAdmin.html + var ipPortPair = address.split(":") + let ip = parseHexIP(ipPortPair[0], family) + let port = Port(parseHexInt(ipPortPair[1])) + return (ip, port) + + +iterator process_inet(file: string, family: int, socketType: int, + inodes: OrderedTable[string, seq[tuple[pid: int, fd: int]]], + filter_pid = -1): Connection = + var laddr, raddr, status, inode: string + var pid, fd: int + + ## Parse /proc/net/tcp* and /proc/net/udp* files. + if file.endsWith("6") and not os.fileExists(file): + # IPv6 not supported + yield Connection() + + for line in file.lines: + try: + let strings = line.splitWhitespace()[..10] + laddr = strings[1] + raddr = strings[2] + status = strings[3] + inode = strings[9] + if laddr == "local_address": + continue + except ValueError: + raise + + if inodes.hasKey(inode): + # # We assume inet sockets are unique, so we error + # # out if there are multiple references to the + # # same inode. We won't do this for UNIX sockets. + # if len( inodes[inode]) > 1 and family != socket.AF_UNIX: + # raise ValueError( "ambiguos inode with multiple " + # "PIDs references" ) + pid = inodes[inode][0].pid + fd = inodes[inode][0].fd -proc get_all_inodes(): OrderedTable[string, seq[tuple[pid:int, fd:int]]] = - result = initOrderedTable[string, seq[tuple[pid:int, fd:int]]]() - for pid in pids(): - try: - let proc_inodes = get_proc_inodes( pid ) - for key, value in proc_inodes: - result[key] = value - except OSError: - # os.listdir() is gonna raise a lot of access denied - # exceptions in case of unprivileged user; that's fine - # as we'll just end up returning a connection with PID - # and fd set to None anyway. - # Both netstat -an and lsof does the same so it's - # unlikely we can do any better. - # ENOENT just means a PID disappeared on us. - let err = ( ref OSError ) getCurrentException() - if err.errorCode.cint in {ENOENT, ESRCH, EPERM, EACCES} == false: - raise - return result + else: + pid = -1 + fd = -1 + if filter_pid != -1 and filter_pid != pid: + continue -proc parseHexIP( ip: string, family: int ): string = - if family == posix.AF_INET: - var ip_int = parseHexInt( ip ).uint32 - result = $inet_ntoa( InAddr( s_addr:ip_int ) ) + else: + if socketType == posix.SOCK_STREAM: + status = TCP_STATUSES[status] + else: + status = "NONE" + let lpair = decode_address(laddr, family) + let rpair = decode_address(raddr, family) + yield Connection(fd: fd, family: family, `type`: socketType, + laddr: lpair.ip, lport: lpair.port, + raddr: rpair.ip, rport: rpair.port, + status: status, pid: pid) + + +iterator process_unix(file: string, family: int, inodes: OrderedTable[string, + seq[tuple[pid: int, fd: int]]], filter_pid = -1): Connection = + ## Parse /proc/net/unix files + for line in file.lines: + let tokens = line.splitWhitespace() + var socketType: string + var inode: string + try: + socketType = tokens[4] + inode = tokens[6] + except ValueError: + if not(" " in line): + # see: https://github.com/giampaolo/psutil/issues/766 + continue + raise newException( + Exception, "error while parsing $1; malformed line $2" % [file, line]) + + # We're parsing the header, skip it + if socketType == "Type": continue + + var pairs: seq[tuple[pid: int, fd: int]] + if inodes.hasKey(inode): + # With UNIX sockets we can have a single inode + # referencing many file descriptors. + pairs = inodes[inode] + else: + pairs = @[(-1, -1)] - elif family == posix.AF_INET6: - var ip_address = IpAddress( family: IpAddressFamily.IPv6 ) - for i in 0..3: - let offset = i * 8 - let piece = ip[offset..offset+7] - var int_piece = parseHexInt( piece ).uint32 - copyMem( addr( ip_address.address_v6[int(offset/2)] ), addr int_piece, 4 ) + for pid_fd_tuple in pairs: + let (pid, fd) = pid_fd_tuple + if filter_pid != -1 and filter_pid != pid: + continue - result = $ip_address + let path = if len(tokens) == 8: tokens[7] else: "" + yield Connection(fd: fd, family: family, `type`: parseInt(socketType), + laddr: path, status: "NONE", pid: pid) - else: - result = ip - - -proc decode_address( address: string, family: int ): tuple[ip:string, port:Port] = - ## Accept an "ip:port" address as displayed in /proc/net/* - ## and convert it into a human readable form, like: - ## "0500000A:0016" -> ( "10.0.0.5", 22 ) - ## "0000000000000000FFFF00000100007F:9E49" -> ( "::ffff:127.0.0.1", 40521 ) - ## The IP address portion is a little or big endian four-byte - ## hexadecimal number; that is, the least significant byte is listed - ## first, so we need to reverse the order of the bytes to convert it - ## to an IP address. - ## The port is represented as a two-byte hexadecimal number. - ## Reference: - ## http://linuxdevcenter.com/pub/a/linux/2000/11/16/LinuxAdmin.html - var ipPortPair = address.split( ":" ) - let ip = parseHexIP( ipPortPair[0], family ) - let port = Port( parseHexInt( ipPortPair[1] ) ) - return ( ip, port ) - - -iterator process_inet( file: string, family: int, socketType: int, inodes : OrderedTable[string, seq[tuple[pid:int, fd:int]]], filter_pid= -1 ) : Connection = - var laddr, raddr, status, inode : string - var pid, fd : int - - ## Parse /proc/net/tcp* and /proc/net/udp* files. - if file.endsWith( "6" ) and not os.fileExists( file ): - # IPv6 not supported - yield Connection() - - for line in file.lines: - try: - let strings = line.splitWhitespace()[..10] - laddr = strings[1] - raddr = strings[2] - status = strings[3] - inode = strings[9] - if laddr == "local_address": - continue - - except ValueError: - raise - - if inodes.hasKey(inode): - # # We assume inet sockets are unique, so we error - # # out if there are multiple references to the - # # same inode. We won't do this for UNIX sockets. - # if len( inodes[inode]) > 1 and family != socket.AF_UNIX: - # raise ValueError( "ambiguos inode with multiple " - # "PIDs references" ) - pid = inodes[inode][0].pid - fd = inodes[inode][0].fd - - else: - pid = -1 - fd = -1 - - if filter_pid != -1 and filter_pid != pid: - continue - - else: - if socketType == posix.SOCK_STREAM: - status = TCP_STATUSES[status] - else: - status = "NONE" - let lpair = decode_address( laddr, family ) - let rpair = decode_address( raddr, family ) - yield Connection( fd: fd, family:family, `type`: socketType, - laddr:lpair.ip, lport:lpair.port, - raddr:rpair.ip, rport:rpair.port, - status:status, pid:pid ) - - -iterator process_unix( file: string, family: int, inodes : OrderedTable[string, seq[tuple[pid:int, fd:int]]], filter_pid= -1 ): Connection = - ## Parse /proc/net/unix files - for line in file.lines: - let tokens = line.splitWhitespace() - var socketType: string - var inode: string - try: - socketType = tokens[4] - inode = tokens[6] - except ValueError: - if not( " " in line ): - # see: https://github.com/giampaolo/psutil/issues/766 - continue - raise newException( - Exception, "error while parsing $1; malformed line $2" % [file, line] ) - - # We're parsing the header, skip it - if socketType == "Type": continue - - var pairs: seq[tuple[pid:int, fd:int]] - if inodes.hasKey(inode): - # With UNIX sockets we can have a single inode - # referencing many file descriptors. - pairs = inodes[inode] - else: - pairs = @[( -1, -1 )] - - for pid_fd_tuple in pairs: - let (pid, fd) = pid_fd_tuple - if filter_pid != -1 and filter_pid != pid: - continue - - let path = if len( tokens ) == 8: tokens[7] else: "" - yield Connection( fd: fd, family: family, `type`: parseInt(socketType), - laddr: path, status: "NONE", pid: pid ) - - -proc net_connections*( kind= "inet", pid= -1 ): seq[Connection] = - var inodes : OrderedTable[string, seq[tuple[pid:int, fd:int]]] - result = newSeq[Connection]() - - if not tmap.hasKey( kind ): - return result - - if pid != -1: - inodes = get_proc_inodes( pid ) - if inodes.len == 0: # no connections for this process - return result - else: - inodes = get_all_inodes() - let conTypes = tmap[kind] - for f, family, socketType in conTypes.items(): - if family in {posix.AF_INET, posix.AF_INET6}: - for conn in process_inet( "/proc/net/$1" % f, family, socketType, inodes, filter_pid=pid ): - result.add( conn ) - else: - for conn in process_unix( "/proc/net/$1" % f, family, inodes, filter_pid=pid ): - result.add( conn ) +proc net_connections*(kind = "inet", pid = -1): seq[Connection] = + var inodes: OrderedTable[string, seq[tuple[pid: int, fd: int]]] + result = newSeq[Connection]() + if not tmap.hasKey(kind): return result + if pid != -1: + inodes = get_proc_inodes(pid) + if inodes.len == 0: # no connections for this process + return result + else: + inodes = get_all_inodes() + + let conTypes = tmap[kind] + for f, family, socketType in conTypes.items(): + if family in {posix.AF_INET, posix.AF_INET6}: + for conn in process_inet("/proc/net/$1" % f, family, socketType, inodes, + filter_pid = pid): + result.add(conn) + else: + for conn in process_unix("/proc/net/$1" % f, family, inodes, + filter_pid = pid): + result.add(conn) + + return result + proc isSsd*(diskLetter: char): bool {.inline.} = ## Returns ``true`` if disk is SSD (Solid). Linux only. @@ -1012,5 +1024,6 @@ proc isSsd*(diskLetter: char): bool {.inline.} = ## .. code-block:: nim ## echo isSsd('a') ## 'a' for /dev/sda, 'b' for /dev/sdb, ... ## - try: readFile("/sys/block/sd" & $diskLetter & "/queue/rotational") == "0\n" except: false + try: readFile("/sys/block/sd" & $diskLetter & "/queue/rotational") == + "0\n" except: false diff --git a/src/psutil/psutil_macosx.nim b/src/psutil/psutil_macosx.nim index d688ad0..51aad13 100644 --- a/src/psutil/psutil_macosx.nim +++ b/src/psutil/psutil_macosx.nim @@ -1,45 +1,45 @@ import posix, segfaults, tables, psutil_posix, nativesockets import times except Time import common -import strutils,sequtils +import strutils, sequtils import ./arch/osx/process_info import ./arch/osx/socket import psutil_posix include "system/ansi_c" template offset*[T](p: ptr T, count: int): ptr T = - ## Offset a pointer to T by count elements. Behavior is undefined on - ## overflow. - - # Actual behavior is wrapping, but this may be revised in the future to enable - # better optimizations. - - let bytes = cast[uint](count) * uint(sizeof(T)) - cast[ptr T](offset(cast[pointer](p), cast[int](bytes))) + ## Offset a pointer to T by count elements. Behavior is undefined on + ## overflow. + + # Actual behavior is wrapping, but this may be revised in the future to enable + # better optimizations. + + let bytes = cast[uint](count) * uint(sizeof(T)) + cast[ptr T](offset(cast[pointer](p), cast[int](bytes))) template offset*(p: pointer, bytes: int): pointer = - ## Offset a memory address by a number of bytes. Behavior is undefined on - ## overflow. - # Actual behavior is wrapping, but this may be revised in the future to enable - # better optimizations - - # We assume two's complement wrapping behaviour for `uint` - cast[pointer](cast[uint](p) + cast[uint](bytes)) + ## Offset a memory address by a number of bytes. Behavior is undefined on + ## overflow. + # Actual behavior is wrapping, but this may be revised in the future to enable + # better optimizations + + # We assume two's complement wrapping behaviour for `uint` + cast[pointer](cast[uint](p) + cast[uint](bytes)) const - CTL_KERN = 1.cint # - KERN_SUCCESS = 0 # mach/kern_return.h. - CTL_VM = 2.cint - KERN_BOOTTIME = 21.cint - # host_statistics() - HOST_LOAD_INFO = 1.cint # System loading stats - HOST_VM_INFO = 2.cint # Virtual memory stats - HOST_CPU_LOAD_INFO = 3 # CPU load stats + CTL_KERN = 1.cint # + KERN_SUCCESS = 0 # mach/kern_return.h. + CTL_VM = 2.cint + KERN_BOOTTIME = 21.cint + # host_statistics() + HOST_LOAD_INFO = 1.cint # System loading stats + HOST_VM_INFO = 2.cint # Virtual memory stats + HOST_CPU_LOAD_INFO = 3 # CPU load stats # host_statistics64() - HOST_VM_INFO64 = 4 # 64-bit virtual memory stats - HOST_EXTMOD_INFO64 = 5 # External modification stats - HOST_EXPIRED_TASK_INFO = 6 # Statistics for expired tasks + HOST_VM_INFO64 = 4 # 64-bit virtual memory stats + HOST_EXTMOD_INFO64 = 5 # External modification stats + HOST_EXPIRED_TASK_INFO = 6 # Statistics for expired tasks var CPU_STATE_MAX {.importc: "CPU_STATE_MAX", header: "".}: cint @@ -60,11 +60,11 @@ var HW_MEMSIZE {.importc: "HW_MEMSIZE", header: "".}: cint type host_cpu_load_info_data_t {.importc: "host_cpu_load_info_data_t", header: "".} = object - cpu_ticks*: array[0..3, cint] #limit to CPU_STATE_MAX but cant refer it as array length at compile time. + cpu_ticks*: array[0..3, cint] #limit to CPU_STATE_MAX but cant refer it as array length at compile time. type Timeval = object - tv_sec*: Time - tv_usec*: int32 + tv_sec*: Time + tv_usec*: int32 type host_info_t = ptr cint type mach_port_t {.importc: "mach_port_t", header: "", nodecl.} = object @@ -73,113 +73,117 @@ type mach_msg_type_number_t {.importc: "mach_msg_type_number_t", type Vmmeter {.importc: "struct vmmeter", header: "", nodecl.} = object - v_swtch: cint - v_intr: cint - v_soft: cint - v_syscall: cint + v_swtch: cint + v_intr: cint + v_soft: cint + v_syscall: cint type VmStatistics {.importc: "struct vm_statistics", header: "", pure, incompleteStruct, nodecl.} = object # https://opensource.apple.com/source/xnu/xnu-3789.51.2/osfmk/mach/vm_statistics.h.auto.html - free_count: cint # of pages free - active_count: cint # of pages active - inactive_count: cint # of pages inactive - wire_count: cint # of pages wired down - pageins: cint - pageouts: cint - #[ + free_count: cint # of pages free + active_count: cint # of pages active + inactive_count: cint # of pages inactive + wire_count: cint # of pages wired down + pageins: cint + pageouts: cint + #[ * NB: speculative pages are already accounted for in "free_count", * so "speculative_count" is the number of "free" pages that are * used to hold data that was read speculatively from disk but * haven't actually been used by anyone so far. *]# - speculative_count: cint # of pages speculative - # type VmStatistics64 {.importc: "struct vm_statistics64", header: "", pure, incompleteStruct,nodecl.} = object - # free_count:cint # of pages free - # active_count:cint # of pages active - # inactive_count:cint # of pages inactive - # wire_count:cint # of pages wired down - #Used by all architectures + speculative_count: cint # of pages speculative + # type VmStatistics64 {.importc: "struct vm_statistics64", header: "", pure, incompleteStruct,nodecl.} = object + # free_count:cint # of pages free + # active_count:cint # of pages active + # inactive_count:cint # of pages inactive + # wire_count:cint # of pages wired down + #Used by all architectures type vm_statistics_t = ptr VmStatistics type vm_statistics_data_t = VmStatistics type xsw_usage {.importc: "struct xsw_usage", header: "", nodecl.} = object - xsu_total: uint64 - xsu_avail: uint64 - xsu_used: uint64 - xsu_pagesize: uint32 - xsu_encrypted: bool + xsu_total: uint64 + xsu_avail: uint64 + xsu_used: uint64 + xsu_pagesize: uint32 + xsu_encrypted: bool type fsid_t = object - val:array[2,cint] + val: array[2, cint] -const MFSNAMELEN = 15 # length of fs type name, not inc. nul */ -const MNAMELEN = 90 # length of buffer for returned name */ -const MFSTYPENAMELEN = 16 # length of fs type name including null */ -const MAXPATHLEN = 1024 +const MFSNAMELEN = 15 # length of fs type name, not inc. nul */ +const MNAMELEN = 90 # length of buffer for returned name */ +const MFSTYPENAMELEN = 16 # length of fs type name including null */ +const MAXPATHLEN = 1024 -type statfs {.importc: "struct statfs", header: "",pure, incompleteStruct,nodecl.} = object # {.importc: "struct statfs", header: "",pure,nodecl.} +type statfs {.importc: "struct statfs", header: "", pure, + incompleteStruct, + nodecl.} = object # {.importc: "struct statfs", header: "",pure,nodecl.} # https://linux.die.net/man/2/statfs - f_bsize: clong - f_iosize: clong - f_blocks: clong - f_bfree: clong - f_bavail: clong - f_files: clong - f_ffree: clong - f_fsid: fsid_t - f_frsize:clong - f_spare: array[5,clong] - f_namelen: clong - # https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/statfs.2.html - f_flags:clong - f_otype: cshort - f_oflags: cshort - f_owner: uint - f_reserved1:cshort - f_type:cshort - f_reserved2:array[2,clong] - f_fstypename:array[MFSNAMELEN,char] # fs type name */ - f_mntonname:array[MNAMELEN,char] # directory on which mounted */ - f_mntfromname:array[MNAMELEN,char] # mounted file system */ - f_reserved3:char # reserved for future use */ - f_reserved4:array[4,clong] # reserved for future use */ - - + f_bsize: clong + f_iosize: clong + f_blocks: clong + f_bfree: clong + f_bavail: clong + f_files: clong + f_ffree: clong + f_fsid: fsid_t + f_frsize: clong + f_spare: array[5, clong] + f_namelen: clong + # https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/statfs.2.html + f_flags: clong + f_otype: cshort + f_oflags: cshort + f_owner: uint + f_reserved1: cshort + f_type: cshort + f_reserved2: array[2, clong] + f_fstypename: array[MFSNAMELEN, char] # fs type name */ + f_mntonname: array[MNAMELEN, char] # directory on which mounted */ + f_mntfromname: array[MNAMELEN, char] # mounted file system */ + f_reserved3: char # reserved for future use */ + f_reserved4: array[4, clong] # reserved for future use */ + + const MNT_SYNCHRONOUS = 0x00000002 # file system written synchronously -const MNT_RDONLY = 0x00000001 # read only filesystem -const MNT_NOEXEC = 0x00000004 # can't exec from filesystem -const MNT_NOSUID = 0x00000008 # don't honor setuid bits on fs -const MNT_NODEV = 0x00000010 # don't interpret special files -const MNT_UNION = 0x00000020 # union with underlying filesystem -const MNT_ASYNC = 0x00000040 # file system written asynchronously -const MNT_CPROTECT = 0x00000080 # file system supports content protection -const MNT_EXPORTED = 0x00000100 # file system is exported -const MNT_QUARANTINE = 0x00000400 # file system is quarantined -const MNT_LOCAL = 0x00001000 # filesystem is stored locally -const MNT_QUOTA = 0x00002000 # quotas are enabled on filesystem -const MNT_ROOTFS = 0x00004000 # identifies the root filesystem -const MNT_DOVOLFS = 0x00008000 # FS supports volfs (deprecated) -const MNT_DONTBROWSE = 0x00100000 # FS is not appropriate path to user data +const MNT_RDONLY = 0x00000001 # read only filesystem +const MNT_NOEXEC = 0x00000004 # can't exec from filesystem +const MNT_NOSUID = 0x00000008 # don't honor setuid bits on fs +const MNT_NODEV = 0x00000010 # don't interpret special files +const MNT_UNION = 0x00000020 # union with underlying filesystem +const MNT_ASYNC = 0x00000040 # file system written asynchronously +const MNT_CPROTECT = 0x00000080 # file system supports content protection +const MNT_EXPORTED = 0x00000100 # file system is exported +const MNT_QUARANTINE = 0x00000400 # file system is quarantined +const MNT_LOCAL = 0x00001000 # filesystem is stored locally +const MNT_QUOTA = 0x00002000 # quotas are enabled on filesystem +const MNT_ROOTFS = 0x00004000 # identifies the root filesystem +const MNT_DOVOLFS = 0x00008000 # FS supports volfs (deprecated) +const MNT_DONTBROWSE = 0x00100000 # FS is not appropriate path to user data const MNT_IGNORE_OWNERSHIP = 0x00200000 # VFS will ignore ownership info on FS objects const MNT_AUTOMOUNTED = 0x00400000 # filesystem was mounted by automounter -const MNT_JOURNALED = 0x00800000 # filesystem is journaled +const MNT_JOURNALED = 0x00800000 # filesystem is journaled const MNT_NOUSERXATTR = 0x01000000 # Don't allow user extended attributes -const MNT_DEFWRITE = 0x02000000 # filesystem should defer writes -const MNT_MULTILABEL = 0x04000000 # MAC support for individual labels -const MNT_NOATIME = 0x10000000 # disable update of file access time -const MNT_UPDATE = 0x00010000 -const MNT_RELOAD = 0x00040000 -const MNT_FORCE = 0x00080000 +const MNT_DEFWRITE = 0x02000000 # filesystem should defer writes +const MNT_MULTILABEL = 0x04000000 # MAC support for individual labels +const MNT_NOATIME = 0x10000000 # disable update of file access time +const MNT_UPDATE = 0x00010000 +const MNT_RELOAD = 0x00040000 +const MNT_FORCE = 0x00080000 -const MNT_CMDFLAGS = (MNT_UPDATE or MNT_RELOAD or MNT_FORCE) +const MNT_CMDFLAGS = (MNT_UPDATE or MNT_RELOAD or MNT_FORCE) const MNT_WAIT = 1 const MNT_NOWAIT = 2 -proc strlcat(dst:ptr UncheckedArray[char];src: cstring ,size:csize_t ) : csize_t {.importc: "strlcat", nodecl.} -proc getfsstat(buf:ptr statfs; bufsize: clong;mode: cint) : cint {.importc: "getfsstat", nodecl.} +proc strlcat(dst: ptr UncheckedArray[char]; src: cstring, + size: csize_t): csize_t {.importc: "strlcat", nodecl.} +proc getfsstat(buf: ptr statfs; bufsize: clong; + mode: cint): cint {.importc: "getfsstat", nodecl.} #https://www.freebsd.org/cgi/man.cgi?query=getfsstat&sektion=2&manpath=freebsd-release-ports proc sysctl(x: pointer, y: cint, z: pointer, @@ -207,23 +211,23 @@ proc host_statistics64(a: mach_port_t; b: cint; c: host_info_t; header: "", nodecl, varargs.} proc boot_time*(): int = - ## Return the system boot time expressed in seconds since the epoch, Integer type. - var - mib: array[0..3, cint] - boot_time: Time - len: csize_t - r: Timeval - mib[0] = CTL_KERN - mib[1] = KERN_BOOTTIME - len = sizeof(r).csize_t - if sysctl(mib.addr, 2, addr(r), len, nil, 0) == -1: - raise newException(OSError, "") - boot_time = (Time)r.tv_sec - return boot_time.int + ## Return the system boot time expressed in seconds since the epoch, Integer type. + var + mib: array[0..3, cint] + boot_time: Time + len: csize_t + r: Timeval + mib[0] = CTL_KERN + mib[1] = KERN_BOOTTIME + len = sizeof(r).csize_t + if sysctl(mib.addr, 2, addr(r), len, nil, 0) == -1: + raise newException(OSError, "") + boot_time = (Time)r.tv_sec + return boot_time.int proc uptime*(): int = - ## Return the system uptime expressed in seconds, Integer type. - times.epochTime().int - boot_time() + ## Return the system uptime expressed in seconds, Integer type. + times.epochTime().int - boot_time() var HOST_CPU_LOAD_INFO_COUNT {.importc: "HOST_CPU_LOAD_INFO_COUNT", header: "", nodecl.}: cint @@ -231,43 +235,43 @@ var HOST_CPU_LOAD_INFO_COUNT {.importc: "HOST_CPU_LOAD_INFO_COUNT", proc cpu_times*(): CPUTimes = - var count = cast[mach_msg_type_number_t](HOST_CPU_LOAD_INFO_COUNT) - let host_port = mach_host_self() - var r_load: host_cpu_load_info_data_t - let error = host_statistics(host_port, HOST_CPU_LOAD_INFO, cast[ - host_info_t](r_load.unsafeAddr), count.addr) + var count = cast[mach_msg_type_number_t](HOST_CPU_LOAD_INFO_COUNT) + let host_port = mach_host_self() + var r_load: host_cpu_load_info_data_t + let error = host_statistics(host_port, HOST_CPU_LOAD_INFO, cast[ + host_info_t](r_load.unsafeAddr), count.addr) - if error != KERN_SUCCESS: - raise newException(OSError, "host_statistics(HOST_CPU_LOAD_INFO) syscall failed: $1" % - $mach_error_string(error)) - mach_port_deallocate(mach_task_self(), host_port) + if error != KERN_SUCCESS: + raise newException(OSError, "host_statistics(HOST_CPU_LOAD_INFO) syscall failed: $1" % + $mach_error_string(error)) + mach_port_deallocate(mach_task_self(), host_port) - result.user = r_load.cpu_ticks[CPU_STATE_USER].cdouble / CLK_TCK - result.nice = r_load.cpu_ticks[CPU_STATE_NICE].cdouble / CLK_TCK - result.system = r_load.cpu_ticks[CPU_STATE_SYSTEM].cdouble / CLK_TCK - result.idle = r_load.cpu_ticks[CPU_STATE_IDLE].cdouble / CLK_TCK + result.user = r_load.cpu_ticks[CPU_STATE_USER].cdouble / CLK_TCK + result.nice = r_load.cpu_ticks[CPU_STATE_NICE].cdouble / CLK_TCK + result.system = r_load.cpu_ticks[CPU_STATE_SYSTEM].cdouble / CLK_TCK + result.idle = r_load.cpu_ticks[CPU_STATE_IDLE].cdouble / CLK_TCK proc cpu_stats*(): tuple[ctx_switches, interrupts, soft_interrupts, syscalls: int] = - var vmstat: Vmmeter + var vmstat: Vmmeter - var count: mach_msg_type_number_t = cast[mach_msg_type_number_t](sizeof( - vmstat) div sizeof(cint)); - let mport = mach_host_self() - let ret = host_statistics(mport, HOST_VM_INFO, cast[host_info_t]( - vmstat.unsafeAddr), count.unsafeAddr) - if ret != KERN_SUCCESS: - raise newException(OSError, "host_statistics(HOST_VM_INFO) syscall failed: $1" % - $mach_error_string(ret)) + var count: mach_msg_type_number_t = cast[mach_msg_type_number_t](sizeof( + vmstat) div sizeof(cint)); + let mport = mach_host_self() + let ret = host_statistics(mport, HOST_VM_INFO, cast[host_info_t]( + vmstat.unsafeAddr), count.unsafeAddr) + if ret != KERN_SUCCESS: + raise newException(OSError, "host_statistics(HOST_VM_INFO) syscall failed: $1" % + $mach_error_string(ret)) - mach_port_deallocate(mach_task_self(), mport) + mach_port_deallocate(mach_task_self(), mport) - return (vmstat.v_swtch.int, vmstat.v_intr.int, vmstat.v_soft.int, - vmstat.v_syscall.int) + return (vmstat.v_swtch.int, vmstat.v_intr.int, vmstat.v_soft.int, + vmstat.v_syscall.int) type StructProc {.importc: "struct proc", header: "", pure, incompleteStruct, nodecl.} = object - p_pid: cint + p_pid: cint var KERN_PROC {.importc: "KERN_PROC", header: "", nodecl.}: cint var KERN_PROC_ALL {.importc: "KERN_PROC_ALL", @@ -275,810 +279,903 @@ var KERN_PROC_ALL {.importc: "KERN_PROC_ALL", type StructKinfoProc {.importc: "struct kinfo_proc", header: "", pure, incompleteStruct, nodecl.} = object # https://opensource.apple.com/source/xnu/xnu-1456.1.26/bsd/sys/sysctl.h.auto.html - kp_proc: StructProc - -proc get_proc_list(procList:ptr ptr StructKinfoProc; - procCount: ptr csize_t): int = - - var - size, size2: csize_t - err: cint - lim = 8 - ptrr: pointer - var mib3 = [CTL_KERN, KERN_PROC, KERN_PROC_ALL] - - assert not isNil(procList) - assert isNil(procList[]) - assert not isNil(procCount) - - procCount[] = 0 - - while lim > 0: - size = 0 - if sysctl(mib3.addr, 3, nil, size, nil, 0) == -1: - # PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_PROC_ALL)") - return 1 - size2 = size + (size shr 3) # add some - if size2 > size: - ptrr = c_malloc(size2) - if (ptrr == nil): - ptrr = c_malloc(size) - else: - size = size2 - else: - ptrr = c_malloc(size) - if ptrr == nil: - # PyErr_NoMemory() - return 1; - if sysctl(mib3.addr, 3, ptrr, size, nil, 0) == -1: - err = errno - c_free(ptrr) - if err != ENOMEM: - # PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_PROC_ALL)") - return 1 - else: - procList[] = cast[ptr StructKinfoProc](ptrr) - procCount[] = size div sizeof(StructKinfoProc).csize_t - if (procCount[] <= 0): - # PyErr_Format(PyExc_RuntimeError, "no PIDs found") - return 1 - - return 0 # success + kp_proc: StructProc + +proc get_proc_list(procList: ptr ptr StructKinfoProc; + procCount: ptr csize_t): int = + + var + size, size2: csize_t + err: cint + lim = 8 + ptrr: pointer + var mib3 = [CTL_KERN, KERN_PROC, KERN_PROC_ALL] + + assert not isNil(procList) + assert isNil(procList[]) + assert not isNil(procCount) + + procCount[] = 0 + + while lim > 0: + size = 0 + if sysctl(mib3.addr, 3, nil, size, nil, 0) == -1: + # PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_PROC_ALL)") + return 1 + size2 = size + (size shr 3) # add some + if size2 > size: + ptrr = c_malloc(size2) + if (ptrr == nil): + ptrr = c_malloc(size) + else: + size = size2 + else: + ptrr = c_malloc(size) + if ptrr == nil: + # PyErr_NoMemory() + return 1; + if sysctl(mib3.addr, 3, ptrr, size, nil, 0) == -1: + err = errno + c_free(ptrr) + if err != ENOMEM: + # PyErr_SetFromOSErrnoWithSyscall("sysctl(KERN_PROC_ALL)") + return 1 + else: + procList[] = cast[ptr StructKinfoProc](ptrr) + procCount[] = size div sizeof(StructKinfoProc).csize_t + if (procCount[] <= 0): + # PyErr_Format(PyExc_RuntimeError, "no PIDs found") + return 1 + + return 0 # success # PyErr_Format(PyExc_RuntimeError, "couldn't collect PIDs list") - lim -= 1 - return 1 + lim -= 1 + return 1 proc pids*(): seq[int] = - var - proclist:ptr StructKinfoProc - orig_address:ptr StructKinfoProc - num_processes: csize_t - idx: csize_t - pid: cint - result = newSeq[int](1) - # proclist = (new StructKinfoProc).unsafeAddr - if isNil(result.unsafeAddr): - # return nil - discard - let proclistdupaddr = proclist.addr - if get_proc_list(proclistdupaddr, num_processes.addr) != 0: - discard # goto error; + var + proclist: ptr StructKinfoProc + orig_address: ptr StructKinfoProc + num_processes: csize_t + idx: csize_t + pid: cint + result = newSeq[int](1) + # proclist = (new StructKinfoProc).unsafeAddr + if isNil(result.unsafeAddr): + # return nil + discard + let proclistdupaddr = proclist.addr + if get_proc_list(proclistdupaddr, num_processes.addr) != 0: + discard # goto error; # save the address of proclist so we can free it later - orig_address = proclist - idx = 0 - while idx < num_processes: - pid = proclist[].kp_proc.p_pid - # if (isNil(pid)): - # discard #goto error; - result.add pid.int - # if (PyList_Append(result, pid)) - # discard# goto error; - # CLEAR(pid); - proclist = proclist.offset(1) - idx.inc - - c_free(orig_address) - return result + orig_address = proclist + idx = 0 + while idx < num_processes: + pid = proclist[].kp_proc.p_pid + # if (isNil(pid)): + # discard #goto error; + result.add pid.int + # if (PyList_Append(result, pid)) + # discard# goto error; + # CLEAR(pid); + proclist = proclist.offset(1) + idx.inc + + c_free(orig_address) + return result proc cpu_count_logical*(): cint = - ## shared with BSD - var - mib: array[0..3, cint] - len: csize_t - r: cint - mib[0] = CTL_HW - mib[1] = HW_NCPU - len = sizeof(r).csize_t - if sysctl(mib.addr, 2, addr(r), len, nil, 0) == -1: - return -1 - else: - return r + ## shared with BSD + var + mib: array[0..3, cint] + len: csize_t + r: cint + mib[0] = CTL_HW + mib[1] = HW_NCPU + len = sizeof(r).csize_t + if sysctl(mib.addr, 2, addr(r), len, nil, 0) == -1: + return -1 + else: + return r proc cpu_count_physical*(): int = - ## just OSX - var - mib: array[0..3, cint] - len: csize_t - r: cint - mib[0] = CTL_HW - mib[1] = HW_AVAILCPU - len = sizeof(r).csize_t - if sysctl(mib.addr, 2, addr(r), len, nil, 0) != 0: - return -1 - else: - return r + ## just OSX + var + mib: array[0..3, cint] + len: csize_t + r: cint + mib[0] = CTL_HW + mib[1] = HW_AVAILCPU + len = sizeof(r).csize_t + if sysctl(mib.addr, 2, addr(r), len, nil, 0) != 0: + return -1 + else: + return r proc sys_vminfo(vmstat: ptr vm_statistics_data_t): int = - var - count: mach_msg_type_number_t = cast[mach_msg_type_number_t](sizeof( - vmstat[]) div sizeof(cint)) - mport = mach_host_self() + var + count: mach_msg_type_number_t = cast[mach_msg_type_number_t](sizeof( + vmstat[]) div sizeof(cint)) + mport = mach_host_self() - let ret = host_statistics(mport, HOST_VM_INFO, cast[host_info_t]( - vmstat), count.addr); - if (ret != KERN_SUCCESS): - raise newException(OSError, "host_statistics(HOST_VM_INFO) syscall failed: $1" % - $mach_error_string(ret)) - # return 0 - mach_port_deallocate(mach_task_self(), mport); - return 1; + let ret = host_statistics(mport, HOST_VM_INFO, cast[host_info_t]( + vmstat), count.addr); + if (ret != KERN_SUCCESS): + raise newException(OSError, "host_statistics(HOST_VM_INFO) syscall failed: $1" % + $mach_error_string(ret)) + # return 0 + mach_port_deallocate(mach_task_self(), mport); + return 1; proc virtual_memory*(): VirtualMemory = - ## Return system virtual memory stats. - ## See: - ## https://opensource.apple.com/source/system_cmds/system_cmds-790/vm_stat.tproj/vm_stat.c.auto.html - var - mib: array[0..2, cint] - len: csize_t - total: uint64 - vm: vm_statistics_data_t - let PAGESIZE = sysconf(SC_PAGE_SIZE) - mib[0] = CTL_HW - mib[1] = HW_MEMSIZE - len = sizeof(total).csize_t - if sysctl(mib.addr, 2, addr(total), len, nil, 0) == -1: - discard - # PyErr_SetFromErrno(PyExc_OSError); - else: - discard - # PyErr_Format( - # PyExc_RuntimeError, "sysctl(HW_MEMSIZE) syscall failed"); - # return nil - let ret = sys_vminfo(vm.addr) - if not 1 == ret: - return - return VirtualMemory(total: total.int, - active: vm.active_count * PAGESIZE, - inactive: vm.inactive_count * PAGESIZE, - # wire: vm.wire_count * PAGESIZE, - free: vm.free_count * PAGESIZE, - # speculative: vm.speculative_count * PAGESIZE - ) + ## Return system virtual memory stats. + ## See: + ## https://opensource.apple.com/source/system_cmds/system_cmds-790/vm_stat.tproj/vm_stat.c.auto.html + var + mib: array[0..2, cint] + len: csize_t + total: uint64 + vm: vm_statistics_data_t + let PAGESIZE = sysconf(SC_PAGE_SIZE) + mib[0] = CTL_HW + mib[1] = HW_MEMSIZE + len = sizeof(total).csize_t + if sysctl(mib.addr, 2, addr(total), len, nil, 0) == -1: + discard + # PyErr_SetFromErrno(PyExc_OSError); + else: + discard + # PyErr_Format( + # PyExc_RuntimeError, "sysctl(HW_MEMSIZE) syscall failed"); + # return nil + let ret = sys_vminfo(vm.addr) + if not 1 == ret: + return + return VirtualMemory(total: total.int, + active: vm.active_count * PAGESIZE, + inactive: vm.inactive_count * PAGESIZE, + # wire: vm.wire_count * PAGESIZE, + free: vm.free_count * PAGESIZE, + # speculative: vm.speculative_count * PAGESIZE + ) proc swap_memory*(): SwapMemory = - var - mib: array[0..2, cint] - len: csize_t - totals: xsw_usage - vm: vm_statistics_data_t - let PAGESIZE = sysconf(SC_PAGE_SIZE) - mib[0] = CTL_HW - mib[1] = HW_MEMSIZE - len = sizeof(totals).csize_t - if sysctl(mib.addr, 2, addr(totals), len, nil, 0) == -1: - discard - # PyErr_SetFromErrno(PyExc_OSError); - else: - discard - # PyErr_Format( - # PyExc_RuntimeError, "sysctl(HW_MEMSIZE) syscall failed"); - # return nil - let ret = sys_vminfo(vm.addr) - if not 1 == ret: - return - let percent = usage_percent(totals.xsu_used, totals.xsu_total, 1) - return SwapMemory(total: totals.xsu_total.int, used: totals.xsu_used.int, - free: totals.xsu_avail.int, - percent: percent, sin: vm.pageins * PAGESIZE, sout: vm.pageouts * PAGESIZE) + var + mib: array[0..2, cint] + len: csize_t + totals: xsw_usage + vm: vm_statistics_data_t + let PAGESIZE = sysconf(SC_PAGE_SIZE) + mib[0] = CTL_HW + mib[1] = HW_MEMSIZE + len = sizeof(totals).csize_t + if sysctl(mib.addr, 2, addr(totals), len, nil, 0) == -1: + discard + # PyErr_SetFromErrno(PyExc_OSError); + else: + discard + # PyErr_Format( + # PyExc_RuntimeError, "sysctl(HW_MEMSIZE) syscall failed"); + # return nil + let ret = sys_vminfo(vm.addr) + if not 1 == ret: + return + let percent = usage_percent(totals.xsu_used, totals.xsu_total, 1) + return SwapMemory(total: totals.xsu_total.int, used: totals.xsu_used.int, + free: totals.xsu_avail.int, + percent: percent, sin: vm.pageins * PAGESIZE, sout: vm.pageouts * PAGESIZE) proc calloc*(p: int, newsize: csize_t): pointer {. importc: "calloc", header: "".} proc disk_partitions*(all = false): seq[DiskPartition] = - var - num:int - i:int - len: clong - flags: clong - opts: array[400, char] - fs:ptr statfs - # get the number of mount points - # Py_BEGIN_ALLOW_THREADS - num = getfsstat(nil, 0, MNT_NOWAIT) - # Py_END_ALLOW_THREADS - # if (num == -1) : + var + num: int + i: int + len: clong + flags: clong + opts: array[400, char] + fs: ptr statfs + # get the number of mount points + # Py_BEGIN_ALLOW_THREADS + num = getfsstat(nil, 0, MNT_NOWAIT) + # Py_END_ALLOW_THREADS + # if (num == -1) : + # PyErr_SetFromErrno(PyExc_OSError); + # goto error; + len = sizeof(fs[]) * num + fs = cast[ptr statfs](c_malloc(len.csize_t)) + if (fs == nil): + discard # PyErr_SetFromErrno(PyExc_OSError); # goto error; - len = sizeof(fs[]) * num - fs = cast[ptr statfs](c_malloc(len.csize_t)) - if (fs == nil) : - discard - # PyErr_SetFromErrno(PyExc_OSError); - # goto error; - # Py_BEGIN_ALLOW_THREADS - num = getfsstat(fs, len.clong, MNT_NOWAIT) - # Py_END_ALLOW_THREADS - if (num == -1) : - discard - # PyErr_SetFromErrno(PyExc_OSError); - # goto error - - var - partition:DiskPartition - device: cstring - mountpoint: cstring - fstype: cstring - popts: cstring - fss:ptr UncheckedArray[statfs] - while i < num : - fss = cast[ptr UncheckedArray[statfs]](fs) - flags = fss[i].f_flags - # see sys/mount.h - if (flags and MNT_RDONLY) != 0: - discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), "ro", sizeof(opts).csize_t) - else: - discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), "rw", sizeof(opts).csize_t) - if (flags and MNT_SYNCHRONOUS) != 0: - discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), ",sync", sizeof(opts).csize_t) - if (flags and MNT_NOEXEC) != 0: - discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), "noexec", sizeof(opts).csize_t) - if (flags and MNT_NOSUID) != 0: - discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), ",nosuid", sizeof(opts).csize_t) - if (flags and MNT_UNION) != 0: - discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), ",union", sizeof(opts).csize_t) - if (flags and MNT_ASYNC) != 0: - discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), ",async", sizeof(opts).csize_t) - if (flags and MNT_EXPORTED) != 0: - discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), ",exported", sizeof(opts).csize_t) - if (flags and MNT_QUARANTINE) != 0: - discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), ",quarantine", sizeof(opts).csize_t) - if (flags and MNT_LOCAL) != 0: - discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), ",local", sizeof(opts).csize_t) - if (flags and MNT_QUOTA) != 0: - discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), ",quota", sizeof(opts).csize_t) - if (flags and MNT_ROOTFS) != 0: - discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), ",rootfs", sizeof(opts).csize_t) - if (flags and MNT_DOVOLFS) != 0: - discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), ",dovolfs", sizeof(opts).csize_t) - if (flags and MNT_DONTBROWSE) != 0: - discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), ",dontbrowse", sizeof(opts).csize_t) - if (flags and MNT_IGNORE_OWNERSHIP) != 0: - discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), ",ignore-ownership", sizeof(opts).csize_t) - if (flags and MNT_AUTOMOUNTED) != 0: - discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), ",automounted", sizeof(opts).csize_t) - if (flags and MNT_JOURNALED) != 0: - discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), ",journaled", sizeof(opts).csize_t) - if (flags and MNT_NOUSERXATTR) != 0: - discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), ",nouserxattr", sizeof(opts).csize_t) - if (flags and MNT_DEFWRITE) != 0: - discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), ",defwrite", sizeof(opts).csize_t) - if (flags and MNT_MULTILABEL) != 0: - discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), ",multilabel", sizeof(opts).csize_t) - if (flags and MNT_NOATIME) != 0: - discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), ",noatime", sizeof(opts).csize_t) - if (flags and MNT_UPDATE) != 0: - discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), ",update", sizeof(opts).csize_t) - if (flags and MNT_RELOAD) != 0: - discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), ",reload", sizeof(opts).csize_t) - if (flags and MNT_FORCE) != 0: - discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), ",force", sizeof(opts).csize_t) - if (flags and MNT_CMDFLAGS) != 0: - discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), ",cmdflags", sizeof(opts).csize_t) - device = cast[cstring](fss[i].f_mntfromname.addr) - mountpoint = cast[cstring](fss[i].f_mntonname.addr) - fstype = cast[cstring](fss[i].f_fstypename.addr) - popts = cast[cstring](opts.addr) - partition = DiskPartition( device: $(device) , mountpoint: $(mountpoint), fstype: $(fstype), opts: $(popts) ) - result.add( partition ) - i.inc + # Py_BEGIN_ALLOW_THREADS + num = getfsstat(fs, len.clong, MNT_NOWAIT) + # Py_END_ALLOW_THREADS + if (num == -1): + discard + # PyErr_SetFromErrno(PyExc_OSError); + # goto error + + var + partition: DiskPartition + device: cstring + mountpoint: cstring + fstype: cstring + popts: cstring + fss: ptr UncheckedArray[statfs] + while i < num: + fss = cast[ptr UncheckedArray[statfs]](fs) + flags = fss[i].f_flags + # see sys/mount.h + if (flags and MNT_RDONLY) != 0: + discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), "ro", sizeof(opts).csize_t) + else: + discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), "rw", sizeof(opts).csize_t) + if (flags and MNT_SYNCHRONOUS) != 0: + discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), ",sync", + sizeof(opts).csize_t) + if (flags and MNT_NOEXEC) != 0: + discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), "noexec", + sizeof(opts).csize_t) + if (flags and MNT_NOSUID) != 0: + discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), ",nosuid", + sizeof(opts).csize_t) + if (flags and MNT_UNION) != 0: + discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), ",union", + sizeof(opts).csize_t) + if (flags and MNT_ASYNC) != 0: + discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), ",async", + sizeof(opts).csize_t) + if (flags and MNT_EXPORTED) != 0: + discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), ",exported", + sizeof(opts).csize_t) + if (flags and MNT_QUARANTINE) != 0: + discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), ",quarantine", + sizeof(opts).csize_t) + if (flags and MNT_LOCAL) != 0: + discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), ",local", + sizeof(opts).csize_t) + if (flags and MNT_QUOTA) != 0: + discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), ",quota", + sizeof(opts).csize_t) + if (flags and MNT_ROOTFS) != 0: + discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), ",rootfs", + sizeof(opts).csize_t) + if (flags and MNT_DOVOLFS) != 0: + discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), ",dovolfs", + sizeof(opts).csize_t) + if (flags and MNT_DONTBROWSE) != 0: + discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), ",dontbrowse", + sizeof(opts).csize_t) + if (flags and MNT_IGNORE_OWNERSHIP) != 0: + discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), + ",ignore-ownership", sizeof(opts).csize_t) + if (flags and MNT_AUTOMOUNTED) != 0: + discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), ",automounted", + sizeof(opts).csize_t) + if (flags and MNT_JOURNALED) != 0: + discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), ",journaled", + sizeof(opts).csize_t) + if (flags and MNT_NOUSERXATTR) != 0: + discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), ",nouserxattr", + sizeof(opts).csize_t) + if (flags and MNT_DEFWRITE) != 0: + discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), ",defwrite", + sizeof(opts).csize_t) + if (flags and MNT_MULTILABEL) != 0: + discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), ",multilabel", + sizeof(opts).csize_t) + if (flags and MNT_NOATIME) != 0: + discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), ",noatime", + sizeof(opts).csize_t) + if (flags and MNT_UPDATE) != 0: + discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), ",update", + sizeof(opts).csize_t) + if (flags and MNT_RELOAD) != 0: + discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), ",reload", + sizeof(opts).csize_t) + if (flags and MNT_FORCE) != 0: + discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), ",force", + sizeof(opts).csize_t) + if (flags and MNT_CMDFLAGS) != 0: + discard strlcat(cast[ptr UncheckedArray[char]](opts.addr), ",cmdflags", + sizeof(opts).csize_t) + device = cast[cstring](fss[i].f_mntfromname.addr) + mountpoint = cast[cstring](fss[i].f_mntonname.addr) + fstype = cast[cstring](fss[i].f_fstypename.addr) + popts = cast[cstring](opts.addr) + partition = DiskPartition(device: $(device), mountpoint: $(mountpoint), + fstype: $(fstype), opts: $(popts)) + result.add(partition) + i.inc const UTX_USERSIZE = 256 const UTX_LINESIZE = 32 const UTX_IDSIZE = 4 const UTX_HOSTSIZE = 256 -const USER_PROCESS = 7 # Normal process. +const USER_PROCESS = 7 # Normal process. type timeval_32 = object - tv_sec: int32 # Seconds. - tv_usec: int32 # Microseconds. - -type utmpx {.importc: "struct utmpx",header: "".}= object - ut_type: cshort # Type of login. - ut_pid: Pid # Process ID of login process. - ut_line: array[UTX_LINESIZE, char] # Devicename. - ut_id: array[UTX_IDSIZE, char] # Inittab ID. - ut_user: array[UTX_USERSIZE, char] # Username. - ut_host: array[UTX_HOSTSIZE, char] # Hostname for remote login. - ut_tv: timeval_32 # Time entry was made. - ut_pad: array[16,uint32] #reserved for future use + tv_sec: int32 # Seconds. + tv_usec: int32 # Microseconds. + +type utmpx {.importc: "struct utmpx", header: "".} = object + ut_type: cshort # Type of login. + ut_pid: Pid # Process ID of login process. + ut_line: array[UTX_LINESIZE, char] # Devicename. + ut_id: array[UTX_IDSIZE, char] # Inittab ID. + ut_user: array[UTX_USERSIZE, char] # Username. + ut_host: array[UTX_HOSTSIZE, char] # Hostname for remote login. + ut_tv: timeval_32 # Time entry was made. + ut_pad: array[16, uint32] #reserved for future use proc getutxent(): ptr utmpx {.header: "".} proc setutxent(): void {.header: "".} proc endutxent(): void {.header: "".} proc users*(): seq[User] = - #Return currently connected users as a list of tuples. - result = newSeq[User]() - setutxent() - var ut = getutxent() - while ut != nil: - let is_user_proc = ut.ut_type == USER_PROCESS - if not is_user_proc: - ut = getutxent() - continue + #Return currently connected users as a list of tuples. + result = newSeq[User]() + setutxent() + var ut = getutxent() + while ut != nil: + let is_user_proc = ut.ut_type == USER_PROCESS + if not is_user_proc: + ut = getutxent() + continue + + var hostname = $ut.ut_host + if hostname == ":0.0" or hostname == ":0": + hostname = "localhost" + + let user_tuple = User(name: ($ut.ut_user.join().strip.replace("\x00", "")), + terminal: ($ut.ut_line.join().strip.replace("\x00", + "")), + started: ut.ut_tv.tv_sec.float) + result.add(user_tuple) + ut = getutxent() + endutxent() + +type processor_info_array_t {.importc: "processor_info_array_t", + header: "", pure, incompleteStruct, nodecl.} = object +type processor_cpu_load_info_data_t {.importc: "processor_cpu_load_info_data_t", + header: "".} = object + cpu_ticks*: array[0..3, cint] + +proc host_processor_info(a: mach_port_t; b: cint; c: ptr cint; + d: ptr processor_info_array_t; + e: ptr mach_msg_type_number_t): cint{.importc: "host_processor_info", + header: "".} + +var PROCESSOR_CPU_LOAD_INFO {.importc: "PROCESSOR_CPU_LOAD_INFO", + header: "".}: cint - var hostname = $ut.ut_host - if hostname == ":0.0" or hostname == ":0": - hostname = "localhost" +proc per_cpu_times*(): seq[CPUTimes] = + ## Return a list of tuples representing the CPU times for every + ## CPU available on the system. + result = newSeq[CPUTimes]() + var + cpu_count: cint + i: int + info_array: processor_info_array_t + info_count: mach_msg_type_number_t + cpu_load_info: ptr processor_cpu_load_info_data_t + user, nice, system, idle: cdouble + + let host_port = mach_host_self() + + let error = host_processor_info(host_port, PROCESSOR_CPU_LOAD_INFO, + cpu_count.addr, info_array.addr, info_count.addr) + if error != KERN_SUCCESS: + raise newException(OSError, "host_processor_info(PROCESSOR_CPU_LOAD_INFO) syscall failed: $1" % + $mach_error_string(error)) + + mach_port_deallocate(mach_task_self(), host_port) + cpu_load_info = cast[ptr processor_cpu_load_info_data_t](info_array) + let info = cast[ptr UnCheckedArray[processor_cpu_load_info_data_t]](cpu_load_info) + while i < cpu_count: + user = info[i].cpu_ticks[CPU_STATE_USER].cdouble / CLK_TCK + nice = info[i].cpu_ticks[CPU_STATE_NICE].cdouble / CLK_TCK + system = info[i].cpu_ticks[CPU_STATE_SYSTEM].cdouble / CLK_TCK + idle = info[i].cpu_ticks[CPU_STATE_IDLE].cdouble / CLK_TCK + result.add CPUTimes(user: user, nice: nice, system: system, idle: idle) + i.inc - let user_tuple = User( name:($ut.ut_user.join().strip.replace("\x00", "")), - terminal:($ut.ut_line.join().strip.replace("\x00", "")), - started:ut.ut_tv.tv_sec.float ) - result.add( user_tuple ) - ut = getutxent() - endutxent() +const + NET_RT_IFLIST2 = 6 + PF_ROUTE = 17 + CTL_NET = 4 + RTM_IFINFO2 = 0x12 + +type if_msghdr {.importc: "struct if_msghdr", header: "", + nodecl.} = object + ifm_msglen: cshort + ifm_version: uint8 + ifm_type: uint8 + +type if_data64 {.importc: "struct if_data64", header: "", + nodecl.} = object + ifi_type: uint8 + ifi_typelen: uint8 + ifi_physical: uint8 + ifi_addrlen: uint8 + ifi_hdrlen: uint8 + ifi_recvquota: uint8 + ifi_xmitquota: uint8 + ifi_unused1: uint8 + ifi_mtu: uint32 + ifi_metric: uint32 + ifi_baudrate: uint32 + ifi_ipackets: uint64 + ifi_ierrors: uint64 + ifi_opackets: uint64 + ifi_oerrors: uint64 + ifi_collisions: uint64 + ifi_ibytes: uint64 + ifi_obytes: uint64 + ifi_imcasts: uint64 + ifi_omcasts: uint64 + ifi_iqdrops: uint64 + ifi_noproto: uint64 + ifi_recvtiming: uint32 + ifi_xmittiming: uint32 + ifi_lastchange: timeval_32 + +type if_msghdr2 {.importc: "struct if_msghdr2", header: "", + nodecl.} = object + ifm_msglen: cshort + ifm_version: uint8 + ifm_type: uint8 + ifm_addrs: cint + ifm_flags: cint + ifm_index: cshort + ifm_snd_len: cint + ifm_snd_maxlen: cint + ifm_snd_drops: cint + ifm_timer: cint + ifm_data: if_data64 + +type sockaddr_dl {.importc: "struct sockaddr_dl", header: "", + nodecl.} = object + sdl_len: uint8 + sdl_family: uint8 + sdl_index: cshort + sdl_type: uint8 + sdl_nlen: uint8 + sdl_alen: uint8 + sdl_slen: uint8 + sdl_data: array[12, char] -type processor_info_array_t {.importc: "processor_info_array_t",header: "",pure, incompleteStruct,nodecl.} = object -type processor_cpu_load_info_data_t {.importc: "processor_cpu_load_info_data_t",header: "".} = object - cpu_ticks*: array[0..3, cint] -proc host_processor_info(a: mach_port_t; b: cint; c: ptr cint;d: ptr processor_info_array_t;e:ptr mach_msg_type_number_t) : cint{.importc:"host_processor_info",header: "".} +proc per_nic_net_io_counters*(): TableRef[string, NetIO] = + ## Return network I/O statistics for every network interface + ## installed on the system as a dict of raw tuples. + result = newTable[string, NetIO]() + var + next: ptr char + buf: ptr char + lim: ptr char + ifm: ptr if_msghdr + mib: array[6, cint] + name: cstring + len: csize_t + nameChars: array[32, char] + if2m: ptr if_msghdr2 + sdl: ptr sockaddr_dl + + 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 + # if isNil(buf): + # discard + # PyErr_NoMemory(); + # goto error; + let ret = sysctl(mib.addr, 6, nil, len, nil, 0) + if ret < 0: + discard + # PyErr_SetFromErrno(PyExc_OSError); + # goto error; + buf = cast[ptr char](c_malloc(len.c_size_t)) + if sysctl(mib.addr, 6, buf, len, nil, 0) < 0: + discard + # PyErr_SetFromErrno(PyExc_OSError); + # goto error; + lim = buf.offset(len.int) + next = buf + while cast[int](next) < cast[int](lim): + ifm = cast[ptr if_msghdr](next) + next = next.offset(ifm.ifm_msglen) + if (ifm.ifm_type == RTM_IFINFO2): + if2m = cast[ptr if_msghdr2](ifm) + sdl = cast[ptr sockaddr_dl](cast[ptr UncheckedArray[if_msghdr2]]( + if2m.offset(1))) + nameChars.addr.zeroMem(32) + nameChars.addr.copyMem(cast[pointer](sdl.sdl_data.addr), sdl.sdl_nlen) + name = cast[cstring](nameChars.addr) + result[ $name] = NetIO( + bytes_sent: if2m[].ifm_data.ifi_obytes.int, + bytes_recv: if2m[].ifm_data.ifi_ibytes.int, + packets_sent: if2m[].ifm_data.ifi_opackets.int, + packets_recv: if2m[].ifm_data.ifi_ipackets.int, + errin: if2m[].ifm_data.ifi_ierrors.int, + errout: if2m[].ifm_data.ifi_oerrors.int, + dropin: if2m[].ifm_data.ifi_iqdrops.int, + dropout: 0 # dropout not supported + ) -var PROCESSOR_CPU_LOAD_INFO {.importc: "PROCESSOR_CPU_LOAD_INFO",header: "".}:cint +proc pid_exists*(pid: int): bool = psutil_posix.pid_exists(pid) -proc per_cpu_times*(): seq[CPUTimes] = - ## Return a list of tuples representing the CPU times for every - ## CPU available on the system. - result = newSeq[CPUTimes]() - var - cpu_count:cint - i:int - info_array: processor_info_array_t - info_count:mach_msg_type_number_t - cpu_load_info:ptr processor_cpu_load_info_data_t - user,nice,system,idle:cdouble - - let host_port = mach_host_self() - - let error = host_processor_info(host_port, PROCESSOR_CPU_LOAD_INFO, - cpu_count.addr, info_array.addr, info_count.addr) - if error != KERN_SUCCESS: - raise newException(OSError, "host_processor_info(PROCESSOR_CPU_LOAD_INFO) syscall failed: $1" % $mach_error_string(error)) - - mach_port_deallocate(mach_task_self(), host_port) - cpu_load_info = cast[ptr processor_cpu_load_info_data_t](info_array) - let info = cast[ptr UnCheckedArray[processor_cpu_load_info_data_t]](cpu_load_info) - while i < cpu_count: - user = info[i].cpu_ticks[CPU_STATE_USER].cdouble / CLK_TCK - nice = info[i].cpu_ticks[CPU_STATE_NICE].cdouble / CLK_TCK - system = info[i].cpu_ticks[CPU_STATE_SYSTEM].cdouble / CLK_TCK - idle = info[i].cpu_ticks[CPU_STATE_IDLE].cdouble / CLK_TCK - result.add CPUTimes(user:user,nice:nice,system:system,idle:idle) - i.inc - -const - NET_RT_IFLIST2 = 6 - PF_ROUTE = 17 - CTL_NET = 4 - RTM_IFINFO2 = 0x12 - -type if_msghdr {.importc: "struct if_msghdr",header: "",nodecl.} = object - ifm_msglen:cshort - ifm_version:uint8 - ifm_type:uint8 - -type if_data64 {.importc: "struct if_data64",header: "",nodecl.} = object - ifi_type: uint8 - ifi_typelen: uint8 - ifi_physical: uint8 - ifi_addrlen: uint8 - ifi_hdrlen: uint8 - ifi_recvquota: uint8 - ifi_xmitquota: uint8 - ifi_unused1: uint8 - ifi_mtu: uint32 - ifi_metric: uint32 - ifi_baudrate: uint32 - ifi_ipackets: uint64 - ifi_ierrors: uint64 - ifi_opackets: uint64 - ifi_oerrors: uint64 - ifi_collisions: uint64 - ifi_ibytes: uint64 - ifi_obytes: uint64 - ifi_imcasts: uint64 - ifi_omcasts: uint64 - ifi_iqdrops: uint64 - ifi_noproto: uint64 - ifi_recvtiming: uint32 - ifi_xmittiming: uint32 - ifi_lastchange: timeval_32 - -type if_msghdr2 {.importc: "struct if_msghdr2",header: "",nodecl.} = object - ifm_msglen:cshort - ifm_version:uint8 - ifm_type:uint8 - ifm_addrs:cint - ifm_flags:cint - ifm_index:cshort - ifm_snd_len:cint - ifm_snd_maxlen:cint - ifm_snd_drops:cint - ifm_timer:cint - ifm_data:if_data64 - -type sockaddr_dl {.importc: "struct sockaddr_dl",header: "",nodecl.} = object - sdl_len:uint8 - sdl_family:uint8 - sdl_index:cshort - sdl_type:uint8 - sdl_nlen:uint8 - sdl_alen:uint8 - sdl_slen:uint8 - sdl_data:array[12,char] - - -proc per_nic_net_io_counters*(): TableRef[string, NetIO] = - ## Return network I/O statistics for every network interface - ## installed on the system as a dict of raw tuples. - result = newTable[string, NetIO]() - var - next: ptr char - buf:ptr char - lim: ptr char - ifm:ptr if_msghdr - mib:array[6,cint] - name :cstring - len:csize_t - nameChars:array[32,char] - if2m:ptr if_msghdr2 - sdl:ptr sockaddr_dl - - 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 - # if isNil(buf): - # discard - # PyErr_NoMemory(); - # goto error; - let ret = sysctl(mib.addr, 6, nil, len, nil, 0) - if ret < 0: - discard - # PyErr_SetFromErrno(PyExc_OSError); - # goto error; - buf = cast[ptr char](c_malloc( len.c_size_t )) - if sysctl(mib.addr, 6, buf, len, nil, 0) < 0: - discard - # PyErr_SetFromErrno(PyExc_OSError); - # goto error; - lim = buf.offset(len.int) - next = buf - while cast[int](next) < cast[int](lim): - ifm = cast[ptr if_msghdr](next) - next = next.offset(ifm.ifm_msglen) - if (ifm.ifm_type == RTM_IFINFO2): - if2m = cast[ptr if_msghdr2](ifm) - sdl = cast[ptr sockaddr_dl](cast[ptr UncheckedArray[if_msghdr2]](if2m.offset(1))) - nameChars.addr.zeroMem(32) - nameChars.addr.copyMem(cast[pointer](sdl.sdl_data.addr),sdl.sdl_nlen) - name = cast[cstring](nameChars.addr) - result[ $name ] = NetIO( - bytes_sent : if2m[].ifm_data.ifi_obytes.int, - bytes_recv : if2m[].ifm_data.ifi_ibytes.int, - packets_sent : if2m[].ifm_data.ifi_opackets.int, - packets_recv : if2m[].ifm_data.ifi_ipackets.int, - errin : if2m[].ifm_data.ifi_ierrors.int, - errout : if2m[].ifm_data.ifi_oerrors.int, - dropin : if2m[].ifm_data.ifi_iqdrops.int, - dropout : 0 # dropout not supported - ) - -proc pid_exists*( pid:int ) :bool = psutil_posix.pid_exists( pid ) - -{.passC:"-fconstant-cfstrings" .} +{.passC: "-fconstant-cfstrings".} {.passL: "-framework CoreFoundation -framework IOKit".} type CFStringEncoding = uint32 # type CFDictionary {.importc:"CFDictionary",header:"".} = object -type CFDictionaryRef {.importc:"CFDictionaryRef",header:"",nodecl.} = ptr object +type CFDictionaryRef {.importc: "CFDictionaryRef", + header: "", nodecl.} = ptr object # type CFMutableDictionary {.importc:"CFMutableDictionary",header:"".} = object -type CFMutableDictionaryRef {.importc:"CFMutableDictionaryRef",header:"",nodecl.} = ptr object -type CFAllocatorRef {.importc:"CFAllocatorRef",header:"",nodecl.} = object -type IOOptionBits {.importc:"IOOptionBits",header:"",nodecl.} = object +type CFMutableDictionaryRef {.importc: "CFMutableDictionaryRef", + header: "", nodecl.} = ptr object +type CFAllocatorRef {.importc: "CFAllocatorRef", + header: "", nodecl.} = object +type IOOptionBits {.importc: "IOOptionBits", + header: "", nodecl.} = object # type CFString {.importc:"CFStringRef",header:"",nodecl.} = object -type CFStringRef {.importc:"CFStringRef",header:"",nodecl.} = ptr object -type CFNumber {.importc:"CFNumber",header:"",nodecl.} = object -type CFNumberRef {.importc:"CFNumberRef",header:"",nodecl.} = ptr object -type CFNumberType {.importc:"CFNumberType",header:"",nodecl.} = distinct cint - -proc CFStringGetCString(theString:CFStringRef,buffer:ptr char,bufferSize:clong ,encoding:CFStringEncoding) {.importc:"CFStringGetCString",header:"",nodecl.} -proc CFSTR(str:cstring):cstring{.importc:"CFSTR",header:"",varargs,nodecl.} # https://developer.apple.com/documentation/corefoundation/cfstr?language=occ -proc CFDictionaryGetValue(theDict:CFDictionaryRef,key:pointer):pointer{.importc:"CFDictionaryGetValue",header:"",nodecl.} -proc CFStringGetSystemEncoding():CFStringEncoding{.importc:"CFStringGetSystemEncoding",header:"",nodecl.} -proc CFNumberGetValue(number:CFNumberRef,theType:CFNumberType,valuePtr:pointer):bool{.importc:"CFNumberGetValue",header:"",nodecl.} -proc CFRelease( cf:ptr object){.importc:"CFRelease",header:"",nodecl.} - -var kIOMediaClass{.importc:"kIOMediaClass",header:"",nodecl.} :cstring - -type io_iterator_t {.importc:"io_iterator_t",header:"",nodecl.} = object -var kIOReturnSuccess {.importc:"kIOReturnSuccess",header:"",nodecl.}:cint -var kIOServicePlane {.importc:"kIOServicePlane",header:"",nodecl.}:cstring -var kCFAllocatorDefault {.importc:"kCFAllocatorDefault",header:"",nodecl.}:CFAllocatorRef +type CFStringRef {.importc: "CFStringRef", + header: "", nodecl.} = ptr object +type CFNumber {.importc: "CFNumber", header: "", + nodecl.} = object +type CFNumberRef {.importc: "CFNumberRef", + header: "", nodecl.} = ptr object +type CFNumberType {.importc: "CFNumberType", + header: "", nodecl.} = distinct cint + +proc CFStringGetCString(theString: CFStringRef, buffer: ptr char, + bufferSize: clong, + encoding: CFStringEncoding) {.importc: "CFStringGetCString", + header: "", nodecl.} +proc CFSTR(str: cstring): cstring{.importc: "CFSTR", + header: "", varargs, + nodecl.} # https://developer.apple.com/documentation/corefoundation/cfstr?language=occ +proc CFDictionaryGetValue(theDict: CFDictionaryRef, + key: pointer): pointer{.importc: "CFDictionaryGetValue", + header: "", nodecl.} +proc CFStringGetSystemEncoding(): CFStringEncoding{.importc: "CFStringGetSystemEncoding", + header: "", nodecl.} +proc CFNumberGetValue(number: CFNumberRef, theType: CFNumberType, + valuePtr: pointer): bool{.importc: "CFNumberGetValue", + header: "", nodecl.} +proc CFRelease(cf: ptr object){.importc: "CFRelease", + header: "", nodecl.} + +var kIOMediaClass{.importc: "kIOMediaClass", + header: "", nodecl.}: cstring + +type io_iterator_t {.importc: "io_iterator_t", header: "", + nodecl.} = object +var kIOReturnSuccess {.importc: "kIOReturnSuccess", + header: "", nodecl.}: cint +var kIOServicePlane {.importc: "kIOServicePlane", header: "", + nodecl.}: cstring +var kCFAllocatorDefault {.importc: "kCFAllocatorDefault", + header: "", nodecl.}: CFAllocatorRef const kNilOptions = 0 const kIOBlockStorageDriverStatisticsKey = "Statistics" const kIOBlockStorageDriverStatisticsReadsKey = "Operations (Read)" const kIOBlockStorageDriverStatisticsWritesKey = "Operations (Write)" const kIOBlockStorageDriverStatisticsBytesReadKey = "Bytes (Read)" -const kIOBlockStorageDriverStatisticsTotalReadTimeKey = "Total Time (Read)" +const kIOBlockStorageDriverStatisticsTotalReadTimeKey = "Total Time (Read)" const kIOBlockStorageDriverStatisticsTotalWriteTimeKey = "Total Time (Write)" type io_object_t = mach_port_t #ipc_port_t type io_registry_entry_t = io_object_t # https://developer.apple.com/documentation/iokit/1514687-ioservicematching?language=occ -proc IOServiceMatching(a:cstring):CFMutableDictionaryRef {.importc:"IOServiceMatching",header:"".} - -proc IOServiceGetMatchingServices(a:mach_port_t,b: CFMutableDictionaryRef,c:pointer):cint {.importc:"IOServiceGetMatchingServices",header:"",nodecl.} -proc IOIteratorNext(it:io_iterator_t): io_object_t {.importc:"IOIteratorNext",header:"",nodecl.} #io_object_t or cint -proc IOObjectRelease(it:io_registry_entry_t): void {.importc:"IOObjectRelease",header:"",nodecl.} -proc IORegistryEntryGetParentEntry(entry:io_registry_entry_t,plane:cstring,parent:ptr io_registry_entry_t): cint {.importc:"IORegistryEntryGetParentEntry",header:"",nodecl.} #io_object_t or cint -proc IOObjectConformsTo(a:io_registry_entry_t,b:cstring): bool {.importc:"IOObjectConformsTo",header:"",nodecl.} -proc IORegistryEntryCreateCFProperties(a:io_registry_entry_t,b:ptr CFMutableDictionaryRef,c:pointer,d:cint) : cint {.importc:"IORegistryEntryCreateCFProperties",header:"",nodecl.} +proc IOServiceMatching(a: cstring): CFMutableDictionaryRef {.importc: "IOServiceMatching", + header: "".} + +proc IOServiceGetMatchingServices(a: mach_port_t, b: CFMutableDictionaryRef, + c: pointer): cint {.importc: "IOServiceGetMatchingServices", + header: "", nodecl.} +proc IOIteratorNext(it: io_iterator_t): io_object_t {.importc: "IOIteratorNext", + header: "", nodecl.} #io_object_t or cint +proc IOObjectRelease(it: io_registry_entry_t): void {.importc: "IOObjectRelease", + header: "", nodecl.} +proc IORegistryEntryGetParentEntry(entry: io_registry_entry_t, plane: cstring, + parent: ptr io_registry_entry_t): cint {.importc: "IORegistryEntryGetParentEntry", + header: "", nodecl.} #io_object_t or cint +proc IOObjectConformsTo(a: io_registry_entry_t, + b: cstring): bool {.importc: "IOObjectConformsTo", + header: "", nodecl.} +proc IORegistryEntryCreateCFProperties(a: io_registry_entry_t, + b: ptr CFMutableDictionaryRef, c: pointer, + d: cint): cint {.importc: "IORegistryEntryCreateCFProperties", + header: "", nodecl.} const kMaxDiskNameSize = 64 -const kIOBSDNameKey = "BSD Name" +const kIOBSDNameKey = "BSD Name" const kCFNumberSInt64Type = CFNumberType(4) const kIOBlockStorageDriverStatisticsBytesWrittenKey = "Bytes (Write)" -proc per_disk_io_counters*(): TableRef[string, DiskIO] = - result = newTable[string, DiskIO]() - var - parent_dict,props_dict,stats_dict:CFDictionaryRef - parent,disk:io_registry_entry_t #or cint - disk_list:io_iterator_t - - var kIOMasterPortDefault: mach_port_t # https://developer.apple.com/documentation/iokit/kiomasterportdefault - var kCFAllocatorDefault:pointer = nil # This is a synonym for NULL. - # Get list of disks - if IOServiceGetMatchingServices(kIOMasterPortDefault, IOServiceMatching(kIOMediaClass),disk_list.addr) != kIOReturnSuccess: +proc per_disk_io_counters*(): TableRef[string, DiskIO] = + result = newTable[string, DiskIO]() + var + parent_dict, props_dict, stats_dict: CFDictionaryRef + parent, disk: io_registry_entry_t #or cint + disk_list: io_iterator_t + + var kIOMasterPortDefault: mach_port_t # https://developer.apple.com/documentation/iokit/kiomasterportdefault + var kCFAllocatorDefault: pointer = nil # This is a synonym for NULL. + # Get list of disks + if IOServiceGetMatchingServices(kIOMasterPortDefault, IOServiceMatching( + kIOMediaClass), disk_list.addr) != kIOReturnSuccess: + discard + # PyErr_SetString( + # PyExc_RuntimeError, "unable to get the list of disks."); + # goto error; + disk = IOIteratorNext(disk_list) + while cast[cint](disk) != 0: + if (IORegistryEntryGetParentEntry(disk, kIOServicePlane, parent.addr) != + kIOReturnSuccess): + # PyErr_SetString(PyExc_RuntimeError, + # "unable to get the disk's parent."); + IOObjectRelease(disk) + # goto error; + if IOObjectConformsTo(parent, "IOBlockStorageDriver"): + if IORegistryEntryCreateCFProperties(disk, cast[ + ptr CFMutableDictionaryRef](parent_dict.addr), kCFAllocatorDefault, + kNilOptions) != kIOReturnSuccess: + # PyErr_SetString(PyExc_RuntimeError, + # "unable to get the parent's properties."); + IOObjectRelease(disk) + IOObjectRelease(parent) + # goto error; + if IORegistryEntryCreateCFProperties(parent, cast[ + ptr CFMutableDictionaryRef](props_dict.addr), kCFAllocatorDefault, + kNilOptions) != kIOReturnSuccess: + # PyErr_SetString(PyExc_RuntimeError, + # "unable to get the parent's properties."); + IOObjectRelease(disk) + IOObjectRelease(parent) + # goto error; + if IORegistryEntryCreateCFProperties(parent, cast[ + ptr CFMutableDictionaryRef](props_dict.addr), kCFAllocatorDefault, + kNilOptions) != kIOReturnSuccess: + # PyErr_SetString(PyExc_RuntimeError, + # "unable to get the parent's properties."); + IOObjectRelease(disk) + IOObjectRelease(parent) + # goto error; + var + disk_name_ref: CFStringRef = cast[CFStringRef](CFDictionaryGetValue( + parent_dict, CFSTR(kIOBSDNameKey))) + disk_name: array[kMaxDiskNameSize, char] + CFStringGetCString(disk_name_ref, cast[ptr char](disk_name.addr), + kMaxDiskNameSize, CFStringGetSystemEncoding()) + stats_dict = cast[CFDictionaryRef](CFDictionaryGetValue(props_dict, CFSTR( + kIOBlockStorageDriverStatisticsKey))) + if isNil(stats_dict.addr): discard - # PyErr_SetString( - # PyExc_RuntimeError, "unable to get the list of disks."); + # PyErr_SetString(PyExc_RuntimeError, + # "Unable to get disk stats."); # goto error; + var + number: CFNumberRef + reads: int64 = 0 + writes: int64 = 0 + read_bytes: int64 = 0 + write_bytes: int64 = 0 + read_time: int64 = 0 + write_time: int64 = 0 + name: cstring + + # Get disk reads/writes + number = cast[CFNumberRef](CFDictionaryGetValue(stats_dict, CFSTR( + kIOBlockStorageDriverStatisticsReadsKey))) + if not isNil(number.addr): + discard CFNumberGetValue(number, kCFNumberSInt64Type, reads.addr) + number = cast[CFNumberRef](CFDictionaryGetValue(stats_dict, CFSTR( + kIOBlockStorageDriverStatisticsWritesKey))) + if not isNil(number.addr): + discard CFNumberGetValue(number, kCFNumberSInt64Type, writes.addr) + # Get disk bytes read/written + number = cast[CFNumberRef](CFDictionaryGetValue(stats_dict, CFSTR( + kIOBlockStorageDriverStatisticsBytesReadKey))) + if not isNil(number.addr): + discard CFNumberGetValue(number, kCFNumberSInt64Type, read_bytes.addr) + number = cast[CFNumberRef](CFDictionaryGetValue(stats_dict, CFSTR( + kIOBlockStorageDriverStatisticsBytesWrittenKey))) + if not isNil(number.addr): + discard CFNumberGetValue(number, kCFNumberSInt64Type, write_bytes.addr) + # Get disk time spent reading/writing (nanoseconds) + number = cast[CFNumberRef](CFDictionaryGetValue(stats_dict, CFSTR( + kIOBlockStorageDriverStatisticsTotalReadTimeKey))) + if not isNil(number.addr): + discard CFNumberGetValue(number, kCFNumberSInt64Type, read_time.addr) + number = cast[CFNumberRef](CFDictionaryGetValue(stats_dict, CFSTR( + kIOBlockStorageDriverStatisticsTotalWriteTimeKey))) + if not isNil(number.addr): + discard CFNumberGetValue(number, kCFNumberSInt64Type, write_time.addr) + # Read/Write time on macOS comes back in nanoseconds and in psutil + # we've standardized on milliseconds so do the conversion. + name = cast[cstring](disk_name.addr) + result[$name] = DiskIO( + read_count: reads.int, write_count: writes.int, + read_bytes: read_bytes.int, write_bytes: write_bytes.int, + read_time: read_time.int, write_time: write_time.int + ) + CFRelease(parent_dict) + IOObjectRelease(parent) + CFRelease(props_dict) + IOObjectRelease(disk) disk = IOIteratorNext(disk_list) - while cast[cint](disk) != 0: - if (IORegistryEntryGetParentEntry(disk, kIOServicePlane, parent.addr) != kIOReturnSuccess): - # PyErr_SetString(PyExc_RuntimeError, - # "unable to get the disk's parent."); - IOObjectRelease(disk) - # goto error; - if IOObjectConformsTo(parent, "IOBlockStorageDriver"): - if IORegistryEntryCreateCFProperties(disk,cast[ptr CFMutableDictionaryRef](parent_dict.addr),kCFAllocatorDefault,kNilOptions ) != kIOReturnSuccess: - # PyErr_SetString(PyExc_RuntimeError, - # "unable to get the parent's properties."); - IOObjectRelease(disk) - IOObjectRelease(parent) - # goto error; - if IORegistryEntryCreateCFProperties(parent,cast[ptr CFMutableDictionaryRef](props_dict.addr),kCFAllocatorDefault,kNilOptions ) != kIOReturnSuccess: - # PyErr_SetString(PyExc_RuntimeError, - # "unable to get the parent's properties."); - IOObjectRelease(disk) - IOObjectRelease(parent) - # goto error; - if IORegistryEntryCreateCFProperties(parent,cast[ptr CFMutableDictionaryRef](props_dict.addr),kCFAllocatorDefault,kNilOptions ) != kIOReturnSuccess: - # PyErr_SetString(PyExc_RuntimeError, - # "unable to get the parent's properties."); - IOObjectRelease(disk) - IOObjectRelease(parent) - # goto error; - var - disk_name_ref:CFStringRef = cast[CFStringRef](CFDictionaryGetValue(parent_dict, CFSTR(kIOBSDNameKey))) - disk_name:array[kMaxDiskNameSize,char] - CFStringGetCString(disk_name_ref, cast[ptr char](disk_name.addr),kMaxDiskNameSize,CFStringGetSystemEncoding()) - stats_dict = cast[CFDictionaryRef](CFDictionaryGetValue(props_dict, CFSTR(kIOBlockStorageDriverStatisticsKey))) - if isNil(stats_dict.addr): - discard - # PyErr_SetString(PyExc_RuntimeError, - # "Unable to get disk stats."); - # goto error; - var - number:CFNumberRef - reads:int64 = 0 - writes:int64 = 0 - read_bytes:int64 = 0 - write_bytes:int64 = 0 - read_time:int64 = 0 - write_time:int64 = 0 - name:cstring - - # Get disk reads/writes - number = cast[CFNumberRef]( CFDictionaryGetValue(stats_dict,CFSTR(kIOBlockStorageDriverStatisticsReadsKey)) ) - if not isNil(number.addr): - discard CFNumberGetValue(number, kCFNumberSInt64Type, reads.addr) - number = cast[CFNumberRef]( CFDictionaryGetValue(stats_dict,CFSTR(kIOBlockStorageDriverStatisticsWritesKey)) ) - if not isNil(number.addr): - discard CFNumberGetValue(number, kCFNumberSInt64Type, writes.addr) - # Get disk bytes read/written - number = cast[CFNumberRef](CFDictionaryGetValue(stats_dict,CFSTR(kIOBlockStorageDriverStatisticsBytesReadKey))) - if not isNil(number.addr): - discard CFNumberGetValue(number, kCFNumberSInt64Type, read_bytes.addr) - number = cast[CFNumberRef](CFDictionaryGetValue( stats_dict, CFSTR(kIOBlockStorageDriverStatisticsBytesWrittenKey))) - if not isNil(number.addr): - discard CFNumberGetValue(number, kCFNumberSInt64Type, write_bytes.addr) - # Get disk time spent reading/writing (nanoseconds) - number = cast[CFNumberRef](CFDictionaryGetValue( stats_dict, CFSTR(kIOBlockStorageDriverStatisticsTotalReadTimeKey))) - if not isNil(number.addr): - discard CFNumberGetValue(number, kCFNumberSInt64Type, read_time.addr) - number = cast[CFNumberRef](CFDictionaryGetValue( stats_dict, CFSTR(kIOBlockStorageDriverStatisticsTotalWriteTimeKey))) - if not isNil(number.addr): - discard CFNumberGetValue(number, kCFNumberSInt64Type, write_time.addr) - # Read/Write time on macOS comes back in nanoseconds and in psutil - # we've standardized on milliseconds so do the conversion. - name = cast[cstring](disk_name.addr) - result[$name] = DiskIO( - read_count:reads.int, write_count:writes.int, - read_bytes:read_bytes.int, write_bytes:write_bytes.int, - read_time:read_time.int, write_time:write_time.int - ) - CFRelease(parent_dict) - IOObjectRelease(parent) - CFRelease(props_dict) - IOObjectRelease(disk) - disk = IOIteratorNext(disk_list) - IOObjectRelease ( cast[io_registry_entry_t](disk_list)) + IOObjectRelease (cast[io_registry_entry_t](disk_list)) type proc_bsdinfo = object - pbi_flags*:uint32 - -proc proc_pidinfo( pid:int, flavor:int, arg:uint64, pti:pointer, size:int ): cint = - errno = 0 - var ret:cint - var retval:int32 - ret = process_info.proc_pidinfo(pid.cint, flavor.cint, arg, pti, size.cint) - if ((ret <= 0) or (cast[culong](ret) < sizeof(pti).culong) ): - # psutil_raise_for_pid(pid, "proc_pidinfo()") - return 0 - return ret.cint + pbi_flags*: uint32 + +proc proc_pidinfo(pid: int, flavor: int, arg: uint64, pti: pointer, + size: int): cint = + errno = 0 + var ret: cint + var retval: int32 + ret = process_info.proc_pidinfo(pid.cint, flavor.cint, arg, pti, size.cint) + if ((ret <= 0) or (cast[culong](ret) < sizeof(pti).culong)): + # psutil_raise_for_pid(pid, "proc_pidinfo()") + return 0 + return ret.cint const PSUTIL_CONN_NONE = 128.cint -proc net_connections*( kind= "inet", pid= -1 ): seq[Connection] = - result = newSeq[Connection]() - var - pidinfo_result:int - iterations:int - i:int - fds_pointer:ptr proc_fdinfo - fdp_pointer:ptr proc_fdinfo - si: socket_fdinfo - nb:cint - laddr:cstring - raddr:cstring - if pid == 0: - return result - elif pid == -1: - discard - else: - pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, nil, 0) - fds_pointer = cast[ptr proc_fdinfo](c_malloc(sizeof(pidinfo_result).c_size_t)) - pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fds_pointer, pidinfo_result) - if (pidinfo_result <= 0): +proc net_connections*(kind = "inet", pid = -1): seq[Connection] = + result = newSeq[Connection]() + var + pidinfo_result: int + iterations: int + i: int + fds_pointer: ptr proc_fdinfo + fdp_pointer: ptr proc_fdinfo + si: socket_fdinfo + nb: cint + laddr: cstring + raddr: cstring + if pid == 0: + return result + elif pid == -1: + discard + else: + pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, nil, 0) + fds_pointer = cast[ptr proc_fdinfo](c_malloc(sizeof( + pidinfo_result).c_size_t)) + pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fds_pointer, pidinfo_result) + if (pidinfo_result <= 0): + discard + # goto error; + iterations = pidinfo_result div PROC_PIDLISTFD_SIZE + while i < iterations: + fdp_pointer = cast[ptr UnCheckedArray[proc_fdinfo]](fds_pointer)[i].addr + if fdp_pointer[].proc_fdtype == PROX_FDTYPE_SOCKET: + errno = 0 + nb = proc_pidfdinfo(pid.cint, fdp_pointer[].proc_fd, + PROC_PIDFDSOCKETINFO, cast[pointer](si.addr), sizeof(si).cint) + if nb <= 0 or nb < sizeof(si): + if errno == EBADF: + continue + + else: + discard + # psutil_raise_for_pid( pid, "proc_pidinfo(PROC_PIDFDSOCKETINFO)"); + # goto error; + var + fd, family, typ, lport, rport, state: cint + lip, rip: array[200, char] + inseq: cint + fd = fdp_pointer[].proc_fd + family = si.psi.soi_family + typ = si.psi.soi_type + # apply filters + # py_family = PyLong_FromLong((long)family); + # inseq = PySequence_Contains(py_af_filter, py_family); + # Py_DECREF(py_family); + # if (inseq == 0) + # continue; + # py_type = PyLong_FromLong((long)type); + # inseq = PySequence_Contains(py_type_filter, py_type); + # Py_DECREF(py_type); + # if inseq == 0: + # continue + if errno != 0: + discard + # PyErr_SetFromErrno(PyExc_OSError); + # goto error; + if family == posix.AF_INET or family == posix.AF_INET6: + if (family == posix.AF_INET): + discard inet_ntop(posix.AF_INET, + cast[ptr InAddr](si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_laddr.ina_46.i46a_addr4.addr), + cast[cstring](lip.addr), + sizeof(lip).int32) + discard inet_ntop(posix.AF_INET, + cast[ptr InAddr](si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_faddr.ina_46.i46a_addr4.addr), + cast[cstring](rip.addr), + sizeof(rip).int32) + else: + discard inet_ntop(posix.AF_INET6, + cast[ptr InAddr](si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_laddr.ina_6.addr), + cast[cstring](lip.addr), + sizeof(lip).int32 + ) + discard inet_ntop(posix.AF_INET6, + cast[ptr InAddr](si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_faddr.ina_6.addr), + cast[cstring](rip.addr), + sizeof(rip).int32 + ) + if (errno != 0): + discard + # PyErr_SetFromOSErrnoWithSyscall("inet_ntop()"); + # goto error; + lport = posix.ntohs(si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_lport.uint16).cint + rport = posix.ntohs(si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_fport.uint16).cint + if (typ == posix.SOCK_STREAM): + state = cast[cint](si.psi.soi_proto.pri_tcp.tcpsi_state) + else: + state = PSUTIL_CONN_NONE + elif (family == posix.AF_UNIX): + laddr = cast[cstring](si.psi.soi_proto.pri_un.unsi_addr.ua_sun.sun_path.addr) + if not isNil(laddr): + discard + # goto error; + raddr = cast[cstring](si.psi.soi_proto.pri_un.unsi_caddr.ua_sun.sun_path.addr) + if not isNil(raddr): discard # goto error; - iterations = pidinfo_result div PROC_PIDLISTFD_SIZE - while i < iterations: - fdp_pointer = cast[ptr UnCheckedArray[ proc_fdinfo]](fds_pointer)[i].addr - if fdp_pointer[].proc_fdtype == PROX_FDTYPE_SOCKET: - errno = 0 - nb = proc_pidfdinfo(pid.cint,fdp_pointer[].proc_fd,PROC_PIDFDSOCKETINFO,cast[pointer](si.addr),sizeof(si).cint) - if nb <= 0 or nb < sizeof(si): - if errno == EBADF: - continue - - else: - discard - # psutil_raise_for_pid( pid, "proc_pidinfo(PROC_PIDFDSOCKETINFO)"); - # goto error; - var - fd, family, typ, lport, rport, state:cint - lip,rip:array[200,char] - inseq:cint - fd = fdp_pointer[].proc_fd - family = si.psi.soi_family - typ = si.psi.soi_type - # apply filters - # py_family = PyLong_FromLong((long)family); - # inseq = PySequence_Contains(py_af_filter, py_family); - # Py_DECREF(py_family); - # if (inseq == 0) - # continue; - # py_type = PyLong_FromLong((long)type); - # inseq = PySequence_Contains(py_type_filter, py_type); - # Py_DECREF(py_type); - # if inseq == 0: - # continue - if errno != 0: - discard - # PyErr_SetFromErrno(PyExc_OSError); - # goto error; - if family == posix.AF_INET or family == posix.AF_INET6: - if (family == posix.AF_INET) : - discard inet_ntop(posix.AF_INET, - cast[ptr InAddr](si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_laddr.ina_46.i46a_addr4.addr), - cast[cstring](lip.addr), - sizeof(lip).int32) - discard inet_ntop(posix.AF_INET, - cast[ptr InAddr](si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_faddr.ina_46.i46a_addr4.addr), - cast[cstring](rip.addr), - sizeof(rip).int32) - else: - discard inet_ntop(posix.AF_INET6, - cast[ptr InAddr](si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_laddr.ina_6.addr), - cast[cstring](lip.addr), - sizeof(lip).int32 - ) - discard inet_ntop(posix.AF_INET6, - cast[ptr InAddr](si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_faddr.ina_6.addr), - cast[cstring](rip.addr), - sizeof(rip).int32 - ) - if (errno != 0) : - discard - # PyErr_SetFromOSErrnoWithSyscall("inet_ntop()"); - # goto error; - lport = posix.ntohs(si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_lport.uint16).cint - rport = posix.ntohs(si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_fport.uint16).cint - if (typ == posix.SOCK_STREAM): - state = cast[cint](si.psi.soi_proto.pri_tcp.tcpsi_state) - else: - state = PSUTIL_CONN_NONE - elif (family == posix.AF_UNIX): - laddr = cast[cstring](si.psi.soi_proto.pri_un.unsi_addr.ua_sun.sun_path.addr) - if not isNil(laddr): - discard - # goto error; - raddr = cast[cstring](si.psi.soi_proto.pri_un.unsi_caddr.ua_sun.sun_path.addr) - if not isNil(raddr): - discard - # goto error; - result.add Connection(fd:fd, - family: family, - type: typ, - laddr: $laddr, - lport: Port(lport), - raddr: $raddr, - rport: Port(rport), - status: $state, - pid:pid) - inc i - cfree(fds_pointer) + result.add Connection(fd: fd, + family: family, + type: typ, + laddr: $laddr, + lport: Port(lport), + raddr: $raddr, + rport: Port(rport), + status: $state, + pid: pid) + inc i + cfree(fds_pointer) proc net_if_stats*(): TableRef[string, NICstats] = - ## Get NIC stats (isup, duplex, speed, mtu). - let names = toSeq( per_nic_net_io_counters().keys() ) - result = newTable[string, NICStats]() - for name in names: - let (duplex, speed) = net_if_duplex_speed( name ) - result[name] = NICStats( isup:net_if_flags( name ), - duplex:duplex, - speed:speed, - mtu:net_if_mtu( name ) ) + ## Get NIC stats (isup, duplex, speed, mtu). + let names = toSeq(per_nic_net_io_counters().keys()) + result = newTable[string, NICStats]() + for name in names: + let (duplex, speed) = net_if_duplex_speed(name) + result[name] = NICStats(isup: net_if_flags(name), + duplex: duplex, + speed: speed, + mtu: net_if_mtu(name)) when isMainModule: - echo boot_time() - echo uptime() - echo cpu_times() - echo cpu_stats() - echo pids() - echo cpu_count_logical() - echo cpu_count_physical() - echo virtual_memory() - echo swap_memory() - echo users() - echo per_cpu_times() - echo disk_partitions() - echo per_nic_net_io_counters() - echo pid_exists(0) - echo per_disk_io_counters() - echo net_connections() - # proc_connections -> net_connections - echo net_if_stats() + echo boot_time() + echo uptime() + echo cpu_times() + echo cpu_stats() + echo pids() + echo cpu_count_logical() + echo cpu_count_physical() + echo virtual_memory() + echo swap_memory() + echo users() + echo per_cpu_times() + echo disk_partitions() + echo per_nic_net_io_counters() + echo pid_exists(0) + echo per_disk_io_counters() + echo net_connections() + # proc_connections -> net_connections + echo net_if_stats() diff --git a/src/psutil/psutil_posix.nim b/src/psutil/psutil_posix.nim index a8b2dc6..ef1c10a 100644 --- a/src/psutil/psutil_posix.nim +++ b/src/psutil/psutil_posix.nim @@ -15,32 +15,32 @@ var IFF_POINTOPOINT* {.header: "".}: uint var NI_MAXHOST* {.header: "".}: cint when bsdPlatform: - var IFF_UP {.header: "".}: uint - var SIOCGIFFLAGS {.header: "".}: uint - var SIOCGIFMTU {.header: "".}: uint + var IFF_UP {.header: "".}: uint + var SIOCGIFFLAGS {.header: "".}: uint + var SIOCGIFMTU {.header: "".}: uint else: - var IFF_UP {.header: "".}: uint - var SIOCGIFFLAGS {.header: "".}: uint - var SIOCGIFMTU {.header: "".}: uint + var IFF_UP {.header: "".}: uint + var SIOCGIFFLAGS {.header: "".}: uint + var SIOCGIFMTU {.header: "".}: uint type ifaddrs = object - pifaddrs: ptr ifaddrs # Next item in list - ifa_name: cstring # Name of interface - ifa_flags: uint # Flags from SIOCGIFFLAGS - ifa_addr: ptr SockAddr # Address of interface - ifa_netmask: ptr SockAddr # Netmask of interface - ifu_broadaddr: ptr SockAddr # Broadcast address of interface - ifa_data: pointer # Address-specific data + pifaddrs: ptr ifaddrs # Next item in list + ifa_name: cstring # Name of interface + ifa_flags: uint # Flags from SIOCGIFFLAGS + ifa_addr: ptr SockAddr # Address of interface + ifa_netmask: ptr SockAddr # Netmask of interface + ifu_broadaddr: ptr SockAddr # Broadcast address of interface + ifa_data: pointer # Address-specific data type sockaddr_ll = object - sll_family: uint16 # Always AF_PACKET - sll_protocol: uint16 # Physical-layer protocol */ - sll_ifindex: int32 # Interface number */ - sll_hatype: uint16 # ARP hardware type */ - ll_pkttype: uint8 # Packet type */ - sll_halen: uint8 # Length of address */ - sll_addr: array[8, uint8] # Physical-layer address */ + sll_family: uint16 # Always AF_PACKET + sll_protocol: uint16 # Physical-layer protocol */ + sll_ifindex: int32 # Interface number */ + sll_hatype: uint16 # ARP hardware type */ + ll_pkttype: uint8 # Packet type */ + sll_halen: uint8 # Length of address */ + sll_addr: array[8, uint8] # Physical-layer address */ type ifmap* = object @@ -49,7 +49,7 @@ type base_addr*: cushort irq*: cuchar dma*: cuchar - port*: cuchar ## # 3 bytes spare + port*: cuchar ## # 3 bytes spare type INNER_C_UNION_9261176668105079294* {.union.} = object @@ -75,253 +75,261 @@ type ################################################################################ -proc ioctl*(f: FileHandle, device: uint, data: pointer): int {.header: "".} -proc getifaddrs( ifap: var ptr ifaddrs ): int {.header: "".} -proc freeifaddrs( ifap: ptr ifaddrs ): void {.header: "".} - -proc psutil_convert_ipaddr(address: ptr SockAddr, family: posix.TSa_Family): string - - -proc pid_exists*( pid: int ): bool = - ## Check whether pid exists in the current process table. - if pid == 0: - # According to "man 2 kill" PID 0 has a special meaning: - # it refers to <> so we don't want to go any further. - # If we get here it means this UNIX platform *does* have - # a process with id 0. - return true +proc ioctl*(f: FileHandle, device: uint, + data: pointer): int {.header: "".} +proc getifaddrs(ifap: var ptr ifaddrs): int {.header: "".} +proc freeifaddrs(ifap: ptr ifaddrs): void {.header: "".} + +proc psutil_convert_ipaddr(address: ptr SockAddr, + family: posix.TSa_Family): string + + +proc pid_exists*(pid: int): bool = + ## Check whether pid exists in the current process table. + if pid == 0: + # According to "man 2 kill" PID 0 has a special meaning: + # it refers to <> so we don't want to go any further. + # If we get here it means this UNIX platform *does* have + # a process with id 0. + return true - let ret_code = kill( pid.int32, 0 ) + let ret_code = kill(pid.int32, 0) - if ret_code == 0: return true + if ret_code == 0: return true - # ESRCH == No such process - if errno == ESRCH: return false + # ESRCH == No such process + if errno == ESRCH: return false - # EPERM clearly means there's a process to deny access to - elif errno == EPERM: return true + # EPERM clearly means there's a process to deny access to + elif errno == EPERM: return true - # According to "man 2 kill" possible error values are - # (EINVAL, EPERM, ESRCH) therefore we should never get - # here. If we do let's be explicit in considering this - # an error. - else: raise newException(OSError, "Unknown error from pid_exists: " & $errno ) + # According to "man 2 kill" possible error values are + # (EINVAL, EPERM, ESRCH) therefore we should never get + # here. If we do let's be explicit in considering this + # an error. + else: raise newException(OSError, "Unknown error from pid_exists: " & $errno) proc net_if_addrs*(): Table[string, seq[Address]] = - ## Return the addresses associated to each NIC (network interface card) - ## installed on the system as a table whose keys are the NIC names and - ## value is a seq of Addresses for each address assigned to the NIC. - ## - ## *family* can be either AF_INET, AF_INET6, AF_LINK, which refers to a MAC address. - ## *address* is the primary address and it is always set. - ## *netmask*, *broadcast* and *ptp* may be ``None``. - ## *ptp* stands for "point to point" and references the destination address on a point to point interface (typically a VPN). - ## *broadcast* and *ptp* are mutually exclusive. - ## *netmask*, *broadcast* and *ptp* are not supported on Windows and are set to nil. - var interfaces : ptr ifaddrs - var current : ptr ifaddrs - let ret_code = getifaddrs( interfaces ) - if ret_code == -1: - echo( "net_if_addrs error: ", strerror( errno ) ) - return result - - result = initTable[string, seq[Address]]() - - current = interfaces - while current != nil: - let name = $current.ifa_name - let family = current.ifa_addr.sa_family - let address = psutil_convert_ipaddr( current.ifa_addr, family ) - let netmask = psutil_convert_ipaddr( current.ifa_netmask, family ) - let bc_or_ptp = psutil_convert_ipaddr( current.ifu_broadaddr, family ) - let broadcast = if (current.ifa_flags and IFF_BROADCAST) != 0: bc_or_ptp else: "" - # ifu_broadcast and ifu_ptp are a union in C, but we don't really care what C calls it - let ptp = if (current.ifa_flags and IFF_POINTOPOINT) != 0: bc_or_ptp else: "" - - if not( name in result ): result[name] = newSeq[Address]() - result[name].add( Address( family: family, # psutil_posix.nim(138, 42) Error: type mismatch: got but expected 'TSa_Family = uint16' - address: address, - netmask: netmask, - broadcast: broadcast, - ptp: ptp ) ) - - current = current.pifaddrs - - freeifaddrs( interfaces ) - - -proc psutil_convert_ipaddr(address: ptr SockAddr, family: posix.TSa_Family): string = - result = newString(NI_MAXHOST) - var addrlen: Socklen - var resultLen: Socklen = NI_MAXHOST.uint32 - - if address == nil: - return "" - - if family.int == AF_INET or family.int == AF_INET6: - if family.int == AF_INET: - addrlen = sizeof(SockAddr_in).uint32 - else: - addrlen = sizeof(SockAddr_in6).uint32 - - let err = getnameinfo( address, addrlen, result, resultLen, nil, 0, NI_NUMERICHOST ) - if err != 0: - # // XXX we get here on FreeBSD when processing 'lo' / AF_INET6 - # // broadcast. Not sure what to do other than returning None. - # // ifconfig does not show anything BTW. - return "" - - else: - return result.strip(chars=Whitespace + {'\x00'}) - - elif defined(linux) and family.int == AF_PACKET: - var hw_address = cast[ptr sockaddr_ll](address) - # TODO - this is going to break on non-Ethernet addresses (e.g. mac firewire - 8 bytes) - # psutil actually handles this, i just wanted to test that it was working - return "$1:$2:$3:$4:$5:$6".format( hw_address.sll_addr[0].int.toHex(2), - hw_address.sll_addr[1].int.toHex(2), - hw_address.sll_addr[2].int.toHex(2), - hw_address.sll_addr[3].int.toHex(2), - hw_address.sll_addr[4].int.toHex(2), - hw_address.sll_addr[5].int.toHex(2) ).tolowerAscii() - - - elif ( defined(freebsd) or defined(openbsd) or defined(darwin) or defined(netbsd) ) and family.int == AF_PACKET: - # struct sockaddr_dl *dladdr = (struct sockaddr_dl *)addr; - # len = dladdr->sdl_alen; - # data = LLADDR(dladdr); - discard - + ## Return the addresses associated to each NIC (network interface card) + ## installed on the system as a table whose keys are the NIC names and + ## value is a seq of Addresses for each address assigned to the NIC. + ## + ## *family* can be either AF_INET, AF_INET6, AF_LINK, which refers to a MAC address. + ## *address* is the primary address and it is always set. + ## *netmask*, *broadcast* and *ptp* may be ``None``. + ## *ptp* stands for "point to point" and references the destination address on a point to point interface (typically a VPN). + ## *broadcast* and *ptp* are mutually exclusive. + ## *netmask*, *broadcast* and *ptp* are not supported on Windows and are set to nil. + var interfaces: ptr ifaddrs + var current: ptr ifaddrs + let ret_code = getifaddrs(interfaces) + if ret_code == -1: + echo("net_if_addrs error: ", strerror(errno)) + return result + + result = initTable[string, seq[Address]]() + + current = interfaces + while current != nil: + let name = $current.ifa_name + let family = current.ifa_addr.sa_family + let address = psutil_convert_ipaddr(current.ifa_addr, family) + let netmask = psutil_convert_ipaddr(current.ifa_netmask, family) + let bc_or_ptp = psutil_convert_ipaddr(current.ifu_broadaddr, family) + let broadcast = if (current.ifa_flags and IFF_BROADCAST) != + 0: bc_or_ptp else: "" + # ifu_broadcast and ifu_ptp are a union in C, but we don't really care what C calls it + let ptp = if (current.ifa_flags and IFF_POINTOPOINT) != + 0: bc_or_ptp else: "" + + if not(name in result): result[name] = newSeq[Address]() + result[name].add(Address(family: family, # psutil_posix.nim(138, 42) Error: type mismatch: got but expected 'TSa_Family = uint16' + address: address, + netmask: netmask, + broadcast: broadcast, + ptp: ptp)) + + current = current.pifaddrs + + freeifaddrs(interfaces) + + +proc psutil_convert_ipaddr(address: ptr SockAddr, + family: posix.TSa_Family): string = + result = newString(NI_MAXHOST) + var addrlen: Socklen + var resultLen: Socklen = NI_MAXHOST.uint32 + + if address == nil: + return "" + + if family.int == AF_INET or family.int == AF_INET6: + if family.int == AF_INET: + addrlen = sizeof(SockAddr_in).uint32 else: - # unknown family - return "" + addrlen = sizeof(SockAddr_in6).uint32 + let err = getnameinfo(address, addrlen, result, resultLen, nil, 0, NI_NUMERICHOST) + if err != 0: + # // XXX we get here on FreeBSD when processing 'lo' / AF_INET6 + # // broadcast. Not sure what to do other than returning None. + # // ifconfig does not show anything BTW. + return "" -proc disk_usage*(path: string): DiskUsage = - ## Return disk usage associated with path. - ## Note: UNIX usually reserves 5% disk space which is not accessible - ## by user. In this function "total" and "used" values reflect the - ## total and used disk space whereas "free" and "percent" represent - ## the "free" and "used percent" user disk space. - - var st: Statvfs - let ret_code = statvfs(path, st) - if ret_code == -1: - raise newException(OSError, "disk_usage error: $1" % [$strerror( errno )] ) - - # Total space which is only available to root (unless changed at system level). - let total = (st.f_blocks * st.f_frsize) - # Remaining free space usable by root. - let avail_to_root = (st.f_bfree * st.f_frsize) - # Remaining free space usable by user. - let avail_to_user = (st.f_bavail * st.f_frsize) - # Total space being used in general. - let used = (total - avail_to_root) - # Total space which is available to user (same as 'total' but for the user). - let total_user = used + avail_to_user - # User usage percent compared to the total amount of space - # the user can use. This number would be higher if compared - # to root's because the user has less space (usually -5%). - let usage_percent_user = usage_percent(used, total_user, places=1) - - # NB: the percentage is -5% than what shown by df due to - # reserved blocks that we are currently not considering: - # https://github.com/giampaolo/psutil/issues/829#issuecomment-223750462 - return DiskUsage(total: total.int, used: used.int, free: avail_to_user.int, percent: usage_percent_user.float) - - -proc ioctlsocket*( iface_name: string, ioctl: uint, ifr: var ifreq ): bool = - ## - let sock = socket(posix.AF_INET, posix.SOCK_DGRAM, 0) - if sock == SocketHandle(-1): return false - var interface_name = iface_name - copyMem( addr ifr.ifr_ifrn.ifrn_name, addr(interface_name[0]), len(iface_name) ) - - let ret = ioctl(sock.cint, ioctl, addr ifr) - if ret == -1: return false - discard close( sock ) - return true + else: + return result.strip(chars = Whitespace + {'\x00'}) + elif defined(linux) and family.int == AF_PACKET: + var hw_address = cast[ptr sockaddr_ll](address) + # TODO - this is going to break on non-Ethernet addresses (e.g. mac firewire - 8 bytes) + # psutil actually handles this, i just wanted to test that it was working + return "$1:$2:$3:$4:$5:$6".format(hw_address.sll_addr[0].int.toHex(2), + hw_address.sll_addr[1].int.toHex(2), + hw_address.sll_addr[2].int.toHex(2), + hw_address.sll_addr[3].int.toHex(2), + hw_address.sll_addr[4].int.toHex(2), + hw_address.sll_addr[5].int.toHex( + 2)).tolowerAscii() -proc net_if_mtu*( name: string ): int = - ## Return NIC MTU. - ## References: http://www.i-scream.org/libstatgrab/ - var ifr: ifreq - if ioctlsocket( name, SIOCGIFMTU, ifr ): - result = ifr.ifr_ifru.ifru_mtu - else: - result = 0 + elif (defined(freebsd) or defined(openbsd) or defined(darwin) or defined( + netbsd)) and family.int == AF_PACKET: + # struct sockaddr_dl *dladdr = (struct sockaddr_dl *)addr; + # len = dladdr->sdl_alen; + # data = LLADDR(dladdr); + discard + else: + # unknown family + return "" -proc net_if_flags*( name: string ): bool = - ## Inspect NIC flags, returns a bool indicating whether the NIC is running. - ## References: http://www.i-scream.org/libstatgrab/ - var ifr: ifreq - if ioctlsocket( name, SIOCGIFFLAGS, ifr ): - result = (ifr.ifr_ifru.ifru_flags and IFF_UP.cshort) != 0 - else: - result = false +proc disk_usage*(path: string): DiskUsage = + ## Return disk usage associated with path. + ## Note: UNIX usually reserves 5% disk space which is not accessible + ## by user. In this function "total" and "used" values reflect the + ## total and used disk space whereas "free" and "percent" represent + ## the "free" and "used percent" user disk space. + + var st: Statvfs + let ret_code = statvfs(path, st) + if ret_code == -1: + raise newException(OSError, "disk_usage error: $1" % [$strerror(errno)]) + + # Total space which is only available to root (unless changed at system level). + let total = (st.f_blocks * st.f_frsize) + # Remaining free space usable by root. + let avail_to_root = (st.f_bfree * st.f_frsize) + # Remaining free space usable by user. + let avail_to_user = (st.f_bavail * st.f_frsize) + # Total space being used in general. + let used = (total - avail_to_root) + # Total space which is available to user (same as 'total' but for the user). + let total_user = used + avail_to_user + # User usage percent compared to the total amount of space + # the user can use. This number would be higher if compared + # to root's because the user has less space (usually -5%). + let usage_percent_user = usage_percent(used, total_user, places = 1) + + # NB: the percentage is -5% than what shown by df due to + # reserved blocks that we are currently not considering: + # https://github.com/giampaolo/psutil/issues/829#issuecomment-223750462 + return DiskUsage(total: total.int, used: used.int, free: avail_to_user.int, + percent: usage_percent_user.float) + + +proc ioctlsocket*(iface_name: string, ioctl: uint, ifr: var ifreq): bool = + ## + let sock = socket(posix.AF_INET, posix.SOCK_DGRAM, 0) + if sock == SocketHandle(-1): return false + var interface_name = iface_name + copyMem(addr ifr.ifr_ifrn.ifrn_name, addr(interface_name[0]), len(iface_name)) + + let ret = ioctl(sock.cint, ioctl, addr ifr) + if ret == -1: return false + discard close(sock) + return true + + +proc net_if_mtu*(name: string): int = + ## Return NIC MTU. + ## References: http://www.i-scream.org/libstatgrab/ + + var ifr: ifreq + if ioctlsocket(name, SIOCGIFMTU, ifr): + result = ifr.ifr_ifru.ifru_mtu + else: + result = 0 + + +proc net_if_flags*(name: string): bool = + ## Inspect NIC flags, returns a bool indicating whether the NIC is running. + ## References: http://www.i-scream.org/libstatgrab/ + + var ifr: ifreq + if ioctlsocket(name, SIOCGIFFLAGS, ifr): + result = (ifr.ifr_ifru.ifru_flags and IFF_UP.cshort) != 0 + else: + result = false ## # net_if_stats() macOS/BSD implementation. ## when bsdPlatform: - {.compile: "arch/bsd_osx.c".} - # import segfaults - import system / ansi_c - proc psutil_get_nic_speed*(ifm_active:cint):cint {.importc: "psutil_get_nic_speed".} - - type ifmediareq {.importc: "struct ifmediareq", header: "", - nodecl,pure.} = object - ifm_name*:array[IFNAMSIZ,char] # if name, e.g. "en0" - ifm_current:cint # current media options - ifm_mask:cint # don't care mask - ifm_status:cint # media status - ifm_active:cint # active options - ifm_count:cint # entries in ifm_ulist array - ifm_ulist:ptr cint # media words - - const SIOCGIFMEDIA = 0xc0286938'u32 - const IFM_FDX = 0x00100000'u32 - const IFM_HDX = 2097152'u32 - - proc net_if_duplex_speed*( nic_name: string ):tuple[ duplex: NicDuplex, speed: int ] = - var - sock:int = -1 - ret:int - duplex:int - speed:int - ifr:ifreq - ifmed:ifmediareq - - sock = socket(posix.AF_INET, posix.SOCK_DGRAM, 0).int - # if (sock == -1) - # return PyErr_SetFromErrno(PyExc_OSError); - # PSUTIL_STRNCPY(ifr.ifr_name, nic_name, sizeof(ifr.ifr_name)); - # https://github.com/giampaolo/psutil/blob/9efb453e7163690c82226be3440cd8cb6bdffb5b/psutil/_psutil_common.h#L19 - copyMem(ifmed.ifm_name.addr,nic_name.cstring,sizeof(ifr.ifr_ifrn.ifrn_name) - 1) - ifmed.ifm_name[sizeof(ifr.ifr_ifrn.ifrn_name) - 1] = '\0' - # speed / duplex - c_memset(cast[pointer](ifmed.addr), 0, sizeof( ifmediareq).csize_t); - # strlcpy(ifmed.ifm_name, nic_name, sizeof(ifmed.ifm_name)); - copyMem(ifmed.ifm_name.addr,nic_name.cstring,sizeof(ifmed.ifm_name)) - ret = ioctl(sock.FileHandle, SIOCGIFMEDIA, ifmed.addr) - if ret == -1: - speed = 0 - duplex = 0 - else: - speed = psutil_get_nic_speed(ifmed.ifm_active) - if ((ifmed.ifm_active or IFM_FDX.cint) == ifmed.ifm_active): - duplex = 2 - elif ((ifmed.ifm_active or IFM_HDX.cint) == ifmed.ifm_active): - duplex = 1 - else: - duplex = 0 - discard close(sock.SocketHandle) - return (duplex.NicDuplex, speed) + {.compile: "arch/bsd_osx.c".} + import system / ansi_c + proc psutil_get_nic_speed*(ifm_active: cint): cint {.importc: "psutil_get_nic_speed".} + + type ifmediareq {.importc: "struct ifmediareq", header: "", + nodecl, pure.} = object + ifm_name*: array[IFNAMSIZ, char] # if name, e.g. "en0" + ifm_current: cint # current media options + ifm_mask: cint # don't care mask + ifm_status: cint # media status + ifm_active: cint # active options + ifm_count: cint # entries in ifm_ulist array + ifm_ulist: ptr cint # media words + + const SIOCGIFMEDIA = 0xc0286938'u32 + const IFM_FDX = 0x00100000'u32 + const IFM_HDX = 2097152'u32 + + proc net_if_duplex_speed*(nic_name: string): tuple[duplex: NicDuplex, speed: int] = + var + sock: int = -1 + ret: int + duplex: int + speed: int + ifr: ifreq + ifmed: ifmediareq + + sock = socket(posix.AF_INET, posix.SOCK_DGRAM, 0).int + # if (sock == -1) + # return PyErr_SetFromErrno(PyExc_OSError); + # PSUTIL_STRNCPY(ifr.ifr_name, nic_name, sizeof(ifr.ifr_name)); + # https://github.com/giampaolo/psutil/blob/9efb453e7163690c82226be3440cd8cb6bdffb5b/psutil/_psutil_common.h#L19 + copyMem(ifmed.ifm_name.addr, nic_name.cstring, sizeof( + ifr.ifr_ifrn.ifrn_name) - 1) + ifmed.ifm_name[sizeof(ifr.ifr_ifrn.ifrn_name) - 1] = '\0' + # speed / duplex + c_memset(cast[pointer](ifmed.addr), 0, sizeof(ifmediareq).csize_t); + # strlcpy(ifmed.ifm_name, nic_name, sizeof(ifmed.ifm_name)); + copyMem(ifmed.ifm_name.addr, nic_name.cstring, sizeof(ifmed.ifm_name)) + ret = ioctl(sock.FileHandle, SIOCGIFMEDIA, ifmed.addr) + if ret == -1: + speed = 0 + duplex = 0 + else: + speed = psutil_get_nic_speed(ifmed.ifm_active) + if ((ifmed.ifm_active or IFM_FDX.cint) == ifmed.ifm_active): + duplex = 2 + elif ((ifmed.ifm_active or IFM_HDX.cint) == ifmed.ifm_active): + duplex = 1 + else: + duplex = 0 + discard close(sock.SocketHandle) + return (duplex.NicDuplex, speed) diff --git a/src/psutil/psutil_windows.nim b/src/psutil/psutil_windows.nim index 41dc8b1..a44e877 100644 --- a/src/psutil/psutil_windows.nim +++ b/src/psutil/psutil_windows.nim @@ -17,784 +17,810 @@ const HI_T = 429.4967296 # Make some constants for process architecture const PROCESS_ARCH_UNKNOWN* = 0 # architecture is unknown -const PROCESS_ARCH_X86* = 1 # architecture is 32 bit -const PROCESS_ARCH_X64* = 2 # architecture is 64 bit +const PROCESS_ARCH_X86* = 1 # architecture is 32 bit +const PROCESS_ARCH_X64* = 2 # architecture is 64 bit # The last 3 fields are unexposed traditionally so this has the potential # to break in the future, but this is how psutil does it too. -type SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION* {.pure.} = object - IdleTime*: LARGE_INTEGER - KernelTime*: LARGE_INTEGER - UserTime*: LARGE_INTEGER - DpcTime*: LARGE_INTEGER - InterruptTime*: LARGE_INTEGER - InterruptCount*: ULONG +type SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION * {.pure.} = object + IdleTime*: LARGE_INTEGER + KernelTime*: LARGE_INTEGER + UserTime*: LARGE_INTEGER + DpcTime*: LARGE_INTEGER + InterruptTime*: LARGE_INTEGER + InterruptCount*: ULONG proc raiseError() = - var error_message: LPWSTR = newStringOfCap( 256 ) - let error_code = GetLastError() - discard FormatMessageW( FORMAT_MESSAGE_FROM_SYSTEM, - NULL, - error_code, - MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ).DWORD, - error_message, - 256, - NULL ) - discard SetErrorMode( 0 ) - raise newException( OSError, "ERROR ($1): $2" % [$error_code, $error_message] ) - - -proc psutil_get_drive_type*( drive_type: UINT ): string = - case drive_type - of DRIVE_FIXED: "fixed" - of DRIVE_CDROM: "cdrom" - of DRIVE_REMOVABLE: "removable" - of DRIVE_UNKNOWN: "unknown" - of DRIVE_NO_ROOT_DIR: "unmounted" - of DRIVE_REMOTE: "remote" - of DRIVE_RAMDISK: "ramdisk" - else: "?" + var error_message: LPWSTR = newStringOfCap(256) + let error_code = GetLastError() + discard FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + error_code, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT).DWORD, + error_message, + 256, + NULL) + discard SetErrorMode(0) + raise newException(OSError, "ERROR ($1): $2" % [$error_code, $error_message]) + + +proc psutil_get_drive_type*(drive_type: UINT): string = + case drive_type + of DRIVE_FIXED: "fixed" + of DRIVE_CDROM: "cdrom" + of DRIVE_REMOVABLE: "removable" + of DRIVE_UNKNOWN: "unknown" + of DRIVE_NO_ROOT_DIR: "unmounted" + of DRIVE_REMOTE: "remote" + of DRIVE_RAMDISK: "ramdisk" + else: "?" proc psutil_get_drive_type*(drive: string): string = - var drive_type = GetDriveType(drive) - case drive_type - of DRIVE_FIXED: "fixed" - of DRIVE_CDROM: "cdrom" - of DRIVE_REMOVABLE: "removable" - of DRIVE_UNKNOWN: "unknown" - of DRIVE_NO_ROOT_DIR: "unmounted" - of DRIVE_REMOTE: "remote" - of DRIVE_RAMDISK: "ramdisk" - else: "?" + var drive_type = GetDriveType(drive) + case drive_type + of DRIVE_FIXED: "fixed" + of DRIVE_CDROM: "cdrom" + of DRIVE_REMOVABLE: "removable" + of DRIVE_UNKNOWN: "unknown" + of DRIVE_NO_ROOT_DIR: "unmounted" + of DRIVE_REMOTE: "remote" + of DRIVE_RAMDISK: "ramdisk" + else: "?" proc getnativearch*(): int = - ## Get the native architecture of the system we are running on - var pGetNativeSystemInfo: SYSTEM_INFO - var nativeArch = PROCESS_ARCH_UNKNOWN + ## Get the native architecture of the system we are running on + var pGetNativeSystemInfo: SYSTEM_INFO + var nativeArch = PROCESS_ARCH_UNKNOWN - GetNativeSystemInfo(pGetNativeSystemInfo.addr) + GetNativeSystemInfo(pGetNativeSystemInfo.addr) - if pGetNativeSystemInfo.isNil: - raiseError() + if pGetNativeSystemInfo.isNil: + raiseError() - case pGetNativeSystemInfo.union1.struct1.wProcessorArchitecture - of PROCESSOR_ARCHITECTURE_AMD64: - ## 64 bit (x64) - # dwNativeArch = PROCESSOR_ARCHITECTURE_AMD64 - nativeArch = PROCESS_ARCH_X64 + case pGetNativeSystemInfo.union1.struct1.wProcessorArchitecture + of PROCESSOR_ARCHITECTURE_AMD64: + ## 64 bit (x64) + # dwNativeArch = PROCESSOR_ARCHITECTURE_AMD64 + nativeArch = PROCESS_ARCH_X64 - of PROCESSOR_ARCHITECTURE_IA64: - # dwNativeArch = PROCESSOR_ARCHITECTURE_IA64 - nativeArch = PROCESS_ARCH_X64 + of PROCESSOR_ARCHITECTURE_IA64: + # dwNativeArch = PROCESSOR_ARCHITECTURE_IA64 + nativeArch = PROCESS_ARCH_X64 - of PROCESSOR_ARCHITECTURE_INTEL: - # 32 bit (x86) - # dwNativeArch = PROCESSOR_ARCHITECTURE_INTEL - nativeArch = PROCESS_ARCH_X64 + of PROCESSOR_ARCHITECTURE_INTEL: + # 32 bit (x86) + # dwNativeArch = PROCESSOR_ARCHITECTURE_INTEL + nativeArch = PROCESS_ARCH_X64 - else: - # dwNativeArch = PROCESSOR_ARCHITECTURE_UNKNOWN - nativeArch = PROCESS_ARCH_UNKNOWN + else: + # dwNativeArch = PROCESSOR_ARCHITECTURE_UNKNOWN + nativeArch = PROCESS_ARCH_UNKNOWN - return nativeArch + return nativeArch proc pids*(): seq[int] = - ## Returns a list of PIDs currently running on the system. - result = newSeq[int]() + ## Returns a list of PIDs currently running on the system. + result = newSeq[int]() - var procArray: seq[DWORD] - var procArrayLen = 0 - # Stores the byte size of the returned array from enumprocesses - var enumReturnSz: DWORD = 0 + var procArray: seq[DWORD] + var procArrayLen = 0 + # Stores the byte size of the returned array from enumprocesses + var enumReturnSz: DWORD = 0 - while enumReturnSz == DWORD( procArrayLen * sizeof(DWORD) ): - procArrayLen += 1024 - procArray = newSeq[DWORD](procArrayLen) + while enumReturnSz == DWORD(procArrayLen * sizeof(DWORD)): + procArrayLen += 1024 + procArray = newSeq[DWORD](procArrayLen) - if EnumProcesses( addr procArray[0], - DWORD( procArrayLen * sizeof(DWORD) ), - addr enumReturnSz ) == 0: - raiseError() - return result + if EnumProcesses(addr procArray[0], + DWORD(procArrayLen * sizeof(DWORD)), + addr enumReturnSz) == 0: + raiseError() + return result - # The number of elements is the returned size / size of each element - let numberOfReturnedPIDs = int( int(enumReturnSz) / sizeof(DWORD) ) - for i in 0.. 0: - opts &= "," - opts &= psutil_get_drive_type( drive_type ) - - result.add( DiskPartition( mountpoint: drive_letter, - device: drive_letter, - fstype: $fs_type, # either FAT, FAT32, NTFS, HPFS, CDFS, UDF or NWFS - opts: opts ) ) - discard SetErrorMode( 0 ) - - -proc disk_usage*( path: string ): DiskUsage = - ## Return disk usage associated with path. - var total, free: ULARGE_INTEGER - - let ret_code = GetDiskFreeSpaceExW( path, nil, addr total, addr free ) - if ret_code != 1: raiseError() - - let used = total.QuadPart - free.QuadPart - let percent = usage_percent( used.int, total.QuadPart.int, places=1 ) - return DiskUsage( total:total.QuadPart.int, used:used.int, - free:free.QuadPart.int, percent:percent ) + if LookupAccountSidW(cast[LPCWSTR](NULL), pUser.User.Sid, wcUser, + dwUserLength.addr, wcDomain, dwDomainLength.addr, peUse.addr) == FALSE: + raiseError() + + let user = wcUser[0..^1] + var retu: string + for c in user: + + if cast[char](c) != '\0': + retu.add(cast[char](c)) + else: + break + + let domain = wcDomain[0..^1] + var retd: string + for c in domain: + if cast[char](c) != '\0': + retd.add(cast[char](c)) + else: + break + + return (retd, retu) + +proc disk_partitions*(all = false): seq[DiskPartition] = + result = newSeq[DiskPartition]() + + # avoid to visualize a message box in case something goes wrong + # see https://github.com/giampaolo/psutil/issues/264 + discard SetErrorMode(SEM_FAILCRITICALERRORS) + + var drive_strings = newWString(256) + let returned_len = GetLogicalDriveStringsW(256, drive_strings) + if returned_len == 0: + raiseError() + return + + let letters = split(strip( $drive_strings, chars = {'\0'}), '\0') + for drive_letter in letters: + let drive_type = GetDriveType(drive_letter) + + # by default we only show hard drives and cd-roms + if not all: + if drive_type == DRIVE_UNKNOWN or + drive_type == DRIVE_NO_ROOT_DIR or + drive_type == DRIVE_REMOTE or + drive_type == DRIVE_RAMDISK: continue + + # floppy disk: skip it by default as it introduces a considerable slowdown. + if drive_type == DRIVE_REMOVABLE and drive_letter == "A:\\": + continue + + + var fs_type: LPWSTR = newString(256) + var pflags: DWORD = 0 + var lpdl: LPCWSTR = drive_letter + let gvi_ret = GetVolumeInformationW(lpdl, + NULL, + DWORD(drive_letter.len), + NULL, + NULL, + addr pflags, + fs_type, + DWORD(256)) + var opts = "" + if gvi_ret == 0: + # We might get here in case of a floppy hard drive, in + # which case the error is ( 21, "device not ready"). + # Let's pretend it didn't happen as we already have + # the drive name and type ('removable'). + SetLastError(0) + else: + opts = if (pflags and FILE_READ_ONLY_VOLUME) != 0: "ro" else: "rw" + + if (pflags and FILE_VOLUME_IS_COMPRESSED) != 0: + opts &= ",compressed" + + if len(opts) > 0: + opts &= "," + opts &= psutil_get_drive_type(drive_type) + + result.add(DiskPartition(mountpoint: drive_letter, + device: drive_letter, + fstype: $fs_type, # either FAT, FAT32, NTFS, HPFS, CDFS, UDF or NWFS + opts: opts)) + discard SetErrorMode(0) + + +proc disk_usage*(path: string): DiskUsage = + ## Return disk usage associated with path. + var total, free: ULARGE_INTEGER + + let ret_code = GetDiskFreeSpaceExW(path, nil, addr total, addr free) + if ret_code != 1: raiseError() + + let used = total.QuadPart - free.QuadPart + let percent = usage_percent(used.int, total.QuadPart.int, places = 1) + return DiskUsage(total: total.QuadPart.int, used: used.int, + free: free.QuadPart.int, percent: percent) proc virtual_memory*(): VirtualMemory = - ## System virtual memory - var memInfo: MEMORYSTATUSEX - memInfo.dwLength = sizeof(MEMORYSTATUSEX).DWORD + ## System virtual memory + var memInfo: MEMORYSTATUSEX + memInfo.dwLength = sizeof(MEMORYSTATUSEX).DWORD - if GlobalMemoryStatusEx( addr memInfo ) == 0: - raiseError() + if GlobalMemoryStatusEx(addr memInfo) == 0: + raiseError() - let used = int(memInfo.ullTotalPhys - memInfo.ullAvailPhys) - let percent = usage_percent( used, memInfo.ullTotalPhys.int, places=1 ) - return VirtualMemory( total: memInfo.ullTotalPhys.int, - avail: memInfo.ullAvailPhys.int, - percent: percent, - used: used, - free: memInfo.ullAvailPhys.int ) + let used = int(memInfo.ullTotalPhys - memInfo.ullAvailPhys) + let percent = usage_percent(used, memInfo.ullTotalPhys.int, places = 1) + return VirtualMemory(total: memInfo.ullTotalPhys.int, + avail: memInfo.ullAvailPhys.int, + percent: percent, + used: used, + free: memInfo.ullAvailPhys.int) proc swap_memory*(): SwapMemory = - ## Swap system memory as a (total, used, free, sin, sout) - var memInfo: MEMORYSTATUSEX - memInfo.dwLength = sizeof(MEMORYSTATUSEX).DWORD + ## Swap system memory as a (total, used, free, sin, sout) + var memInfo: MEMORYSTATUSEX + memInfo.dwLength = sizeof(MEMORYSTATUSEX).DWORD - if GlobalMemoryStatusEx( addr memInfo ) == 0: - raiseError() + if GlobalMemoryStatusEx(addr memInfo) == 0: + raiseError() - let total = memInfo.ullTotalPageFile.int - let free = memInfo.ullAvailPageFile.int - let used = total - free - let percent = usage_percent(used, total, places=1) - return SwapMemory(total:total, used:used, free:free, percent:percent, sin:0, sout:0) + let total = memInfo.ullTotalPageFile.int + let free = memInfo.ullAvailPageFile.int + let used = total - free + let percent = usage_percent(used, total, places = 1) + return SwapMemory(total: total, used: used, free: free, percent: percent, + sin: 0, sout: 0) proc toUnixTime(ft: FILETIME): float = - # HUGE thanks to: - # http://johnstewien.spaces.live.com/blog/cns!E6885DB5CEBABBC8!831.entry - # This function converts the FILETIME structure to the 32 bit - # Unix time structure. - # The time_t is a 32-bit value for the number of seconds since - # January 1, 1970. A FILETIME is a 64-bit for the number of - # 100-nanosecond periods since January 1, 1601. Convert by - # subtracting the number of 100-nanosecond period between 01-01-1970 - # and 01-01-1601, from time_t then divide by 1e+7 to get to the same - # base granularity. - let ll = (int64(ft.dwHighDateTime) shl 32) + int64(ft.dwLowDateTime) - result = int(ll - 116444736000000000) / 10000000 + # HUGE thanks to: + # http://johnstewien.spaces.live.com/blog/cns!E6885DB5CEBABBC8!831.entry + # This function converts the FILETIME structure to the 32 bit + # Unix time structure. + # The time_t is a 32-bit value for the number of seconds since + # January 1, 1970. A FILETIME is a 64-bit for the number of + # 100-nanosecond periods since January 1, 1601. Convert by + # subtracting the number of 100-nanosecond period between 01-01-1970 + # and 01-01-1601, from time_t then divide by 1e+7 to get to the same + # base granularity. + let ll = (int64(ft.dwHighDateTime) shl 32) + int64(ft.dwLowDateTime) + result = int(ll - 116444736000000000) / 10000000 proc boot_time*(): float = - ## Return the system boot time expressed in seconds since the epoch - var fileTime : FILETIME - GetSystemTimeAsFileTime(addr fileTime) + ## Return the system boot time expressed in seconds since the epoch + var fileTime: FILETIME + GetSystemTimeAsFileTime(addr fileTime) - let pt = toUnixTime(fileTime) - let uptime = int(GetTickCount64()) / 1000 + let pt = toUnixTime(fileTime) + let uptime = int(GetTickCount64()) / 1000 - return pt - uptime + return pt - uptime proc uptime*(): int = - ## Return the system uptime expressed in seconds, Integer type. - int(GetTickCount64().float / 1000.float) + ## Return the system uptime expressed in seconds, Integer type. + int(GetTickCount64().float / 1000.float) proc per_cpu_times*(): seq[CPUTimes] = - ## Return system per-CPU times as a sequence of CPUTimes. - - let ncpus = GetActiveProcessorCount(ALL_PROCESSOR_GROUPS) - if ncpus == 0: - return result - - # allocates an array of _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION structures, one per processor - var sppi = newSeq[SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION](ncpus) - let buffer_size = ULONG(ncpus * sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)) - - # gets cpu time informations - let status = NtQuerySystemInformation(systemProcessorPerformanceInformation, addr sppi[0], buffer_size, NULL) - if status != 0: - raiseError() - - # computes system global times summing each processor value - for i in 0 ..< ncpus: - let user = (HI_T * sppi[i].UserTime.HighPart.float) + - (LO_T * sppi[i].UserTime.LowPart.float) - let idle = (HI_T * sppi[i].IdleTime.HighPart.float) + - (LO_T * sppi[i].IdleTime.LowPart.float) - let kernel = (HI_T * sppi[i].KernelTime.HighPart.float) + - (LO_T * sppi[i].KernelTime.LowPart.float) - let interrupt = (HI_T * sppi[i].InterruptTime.HighPart.float) + - (LO_T * sppi[i].InterruptTime.LowPart.float) - let dpc = (HI_T * sppi[i].DpcTime.HighPart.float) + - (LO_T * sppi[i].DpcTime.LowPart.float) - - # kernel time includes idle time on windows - # we return only busy kernel time subtracting idle time from kernel time - let system = kernel - idle + ## Return system per-CPU times as a sequence of CPUTimes. + + let ncpus = GetActiveProcessorCount(ALL_PROCESSOR_GROUPS) + if ncpus == 0: + return result + + # allocates an array of _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION structures, one per processor + var sppi = newSeq[SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION](ncpus) + let buffer_size = ULONG(ncpus * sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)) + + # gets cpu time informations + let status = NtQuerySystemInformation(systemProcessorPerformanceInformation, + addr sppi[0], buffer_size, NULL) + if status != 0: + raiseError() + + # computes system global times summing each processor value + for i in 0 ..< ncpus: + let user = (HI_T * sppi[i].UserTime.HighPart.float) + + (LO_T * sppi[i].UserTime.LowPart.float) + let idle = (HI_T * sppi[i].IdleTime.HighPart.float) + + (LO_T * sppi[i].IdleTime.LowPart.float) + let kernel = (HI_T * sppi[i].KernelTime.HighPart.float) + + (LO_T * sppi[i].KernelTime.LowPart.float) + let interrupt = (HI_T * sppi[i].InterruptTime.HighPart.float) + + (LO_T * sppi[i].InterruptTime.LowPart.float) + let dpc = (HI_T * sppi[i].DpcTime.HighPart.float) + + (LO_T * sppi[i].DpcTime.LowPart.float) + + # kernel time includes idle time on windows + # we return only busy kernel time subtracting idle time from kernel time + let system = kernel - idle - result.add(CPUTimes(user:user, system:system, idle:idle, interrupt:interrupt, dpc:dpc)) + result.add(CPUTimes(user: user, system: system, idle: idle, + interrupt: interrupt, dpc: dpc)) proc cpu_times*(): CPUTimes = - ## Retrieves system CPU timing information . On a multiprocessor system, - ## the values returned are the - ## sum of the designated times across all processors. + ## Retrieves system CPU timing information . On a multiprocessor system, + ## the values returned are the + ## sum of the designated times across all processors. - var idle_time: FILETIME - var kernel_time: FILETIME - var user_time: FILETIME + var idle_time: FILETIME + var kernel_time: FILETIME + var user_time: FILETIME - if GetSystemTimes(addr idle_time, addr kernel_time, addr user_time).bool == false: - raiseError() + if GetSystemTimes(addr idle_time, addr kernel_time, addr user_time).bool == false: + raiseError() - let idle = (HI_T * idle_time.dwHighDateTime.float) + (LO_T * idle_time.dwLowDateTime.float) - let user = (HI_T * user_time.dwHighDateTime.float) + (LO_T * user_time.dwLowDateTime.float) - let kernel = (HI_T * kernel_time.dwHighDateTime.float) + (LO_T * kernel_time.dwLowDateTime.float) + let idle = (HI_T * idle_time.dwHighDateTime.float) + (LO_T * + idle_time.dwLowDateTime.float) + let user = (HI_T * user_time.dwHighDateTime.float) + (LO_T * + user_time.dwLowDateTime.float) + let kernel = (HI_T * kernel_time.dwHighDateTime.float) + (LO_T * + kernel_time.dwLowDateTime.float) - # Kernel time includes idle time. - # We return only busy kernel time subtracting idle time from kernel time. - let system = kernel - idle + # Kernel time includes idle time. + # We return only busy kernel time subtracting idle time from kernel time. + let system = kernel - idle - # Internally, GetSystemTimes() is used, and it doesn't return interrupt and dpc times. - # per_cpu_times() does, so we rely on it to get those only. - let per_times = per_cpu_times() - let interrupt_sum = sum(per_times.mapIt(it.interrupt)) - let dpc_sum = sum(per_times.mapIt(it.dpc)) - return CPUTimes(user:user, system:system, idle:idle, interrupt:interrupt_sum, dpc:dpc_sum) + # Internally, GetSystemTimes() is used, and it doesn't return interrupt and dpc times. + # per_cpu_times() does, so we rely on it to get those only. + let per_times = per_cpu_times() + let interrupt_sum = sum(per_times.mapIt(it.interrupt)) + let dpc_sum = sum(per_times.mapIt(it.dpc)) + return CPUTimes(user: user, system: system, idle: idle, + interrupt: interrupt_sum, dpc: dpc_sum) proc cpu_count_logical*(): int = - return cast[int](GetActiveProcessorCount(ALL_PROCESSOR_GROUPS)) + return cast[int](GetActiveProcessorCount(ALL_PROCESSOR_GROUPS)) proc cpu_count_physical*(): int = - var length: DWORD = 0 - var rc = GetLogicalProcessorInformationEx(relationAll, NULL, addr length) + var length: DWORD = 0 + var rc = GetLogicalProcessorInformationEx(relationAll, NULL, addr length) - var buffer = cast[PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX](alloc0(length)) - rc = GetLogicalProcessorInformationEx(relationAll, buffer, addr length) + var buffer = cast[PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX](alloc0(length)) + rc = GetLogicalProcessorInformationEx(relationAll, buffer, addr length) - if rc == 0: - dealloc(buffer) - raiseError() + if rc == 0: + dealloc(buffer) + raiseError() - var currentPtr = buffer - var offset = 0 - var prevProcessorInfoSize = 0 - while offset < length: - # Advance ptr by the size of the previous SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX struct. - currentPtr = cast[PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX](cast[int](currentPtr) + prevProcessorInfoSize) + var currentPtr = buffer + var offset = 0 + var prevProcessorInfoSize = 0 + while offset < length: + # Advance ptr by the size of the previous SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX struct. + currentPtr = cast[PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX](cast[int]( + currentPtr) + prevProcessorInfoSize) - if currentPtr.Relationship == relationProcessorCore: - result += 1 + if currentPtr.Relationship == relationProcessorCore: + result += 1 - # When offset == length, we've reached the last processor info struct in the buffer. - offset += currentPtr.Size - prevProcessorInfoSize = currentPtr.Size + # When offset == length, we've reached the last processor info struct in the buffer. + offset += currentPtr.Size + prevProcessorInfoSize = currentPtr.Size - dealloc(buffer) + dealloc(buffer) type WTS_CONNECTSTATE_CLASS {.pure.} = enum - WTSActive, - WTSConnected, - WTSConnectQuery, - WTSShadow, - WTSDisconnected, - WTSIdle, - WTSListen, - WTSReset, - WTSDown, - WTSInit + WTSActive, + WTSConnected, + WTSConnectQuery, + WTSShadow, + WTSDisconnected, + WTSIdle, + WTSListen, + WTSReset, + WTSDown, + WTSInit type WTS_SESSION_INFO = object - sessionId: DWORD - pWinStationName: LPWSTR - state: WTS_CONNECTSTATE_CLASS + sessionId: DWORD + pWinStationName: LPWSTR + state: WTS_CONNECTSTATE_CLASS type PWTS_SESSION_INFO = ptr WTS_SESSION_INFO type WTS_CLIENT_ADDRESS = object - addressFamily: DWORD - address: array[20, BYTE] + addressFamily: DWORD + address: array[20, BYTE] type PWTS_CLIENT_ADDRESS = ptr WTS_CLIENT_ADDRESS @@ -804,47 +830,47 @@ const WTS_CURRENT_SERVER_HANDLE: HANDLE = 0 type WTS_INFO_CLASS {.pure.} = enum - WTSInitialProgram = 0, - WTSApplicationName = 1, - WTSWorkingDirectory = 2, - WTSOEMId = 3, - WTSSessionId = 4, - WTSUserName = 5, - WTSWinStationName = 6, - WTSDomainName = 7, - WTSConnectState = 8, - WTSClientBuildNumber = 9, - WTSClientName = 10, - WTSClientDirectory = 11, - WTSClientProductId = 12, - WTSClientHardwareId = 13, - WTSClientAddress = 14, - WTSClientDisplay = 15, - WTSClientProtocolType = 16, - WTSIdleTime = 17, - WTSLogonTime = 18, - WTSIncomingBytes = 19, - WTSOutgoingBytes = 20, - WTSIncomingFrames = 21, - WTSOutgoingFrames = 22, - WTSClientInfo = 23, - WTSSessionInfo = 24 + WTSInitialProgram = 0, + WTSApplicationName = 1, + WTSWorkingDirectory = 2, + WTSOEMId = 3, + WTSSessionId = 4, + WTSUserName = 5, + WTSWinStationName = 6, + WTSDomainName = 7, + WTSConnectState = 8, + WTSClientBuildNumber = 9, + WTSClientName = 10, + WTSClientDirectory = 11, + WTSClientProductId = 12, + WTSClientHardwareId = 13, + WTSClientAddress = 14, + WTSClientDisplay = 15, + WTSClientProtocolType = 16, + WTSIdleTime = 17, + WTSLogonTime = 18, + WTSIncomingBytes = 19, + WTSOutgoingBytes = 20, + WTSIncomingFrames = 21, + WTSOutgoingFrames = 22, + WTSClientInfo = 23, + WTSSessionInfo = 24 type WINSTATION_INFO_CLASS = enum - WinStationInformation = 8 + WinStationInformation = 8 type WINSTATION_INFO = object - Reserved1: array[72, BYTE] - SessionId: ULONG - Reserved2: array[4, BYTE] - ConnectTime: FILETIME - DisconnectTime: FILETIME - LastInputTime: FILETIME - LoginTime: FILETIME - Reserved3: array[1096, BYTE] - CurrentTime: FILETIME + Reserved1: array[72, BYTE] + SessionId: ULONG + Reserved2: array[4, BYTE] + ConnectTime: FILETIME + DisconnectTime: FILETIME + LastInputTime: FILETIME + LoginTime: FILETIME + Reserved3: array[1096, BYTE] + CurrentTime: FILETIME proc WTSEnumerateSessionsW( @@ -872,122 +898,129 @@ proc WinStationQueryInformation( winStationInformationClass: WINSTATION_INFO_CLASS, pWinStationInformation: ptr WINSTATION_INFO, winStationInformationLength: ULONG, - pReturnLength: PULONG): BOOLEAN {.winapi, stdcall, dynlib: "winsta", importc: "WinStationQueryInformationW".} + pReturnLength: PULONG): BOOLEAN {.winapi, stdcall, dynlib: "winsta", + importc: "WinStationQueryInformationW".} proc getUserForSession(server: HANDLE, sessionId: DWORD): string = - var buffer_user: PWCHAR = NULL - var bytes: DWORD = 0 - if WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, sessionId, WTSUserName, addr buffer_user, addr bytes) == 0: - raiseError() + var buffer_user: PWCHAR = NULL + var bytes: DWORD = 0 + if WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, sessionId, + WTSUserName, addr buffer_user, addr bytes) == 0: + raiseError() - if bytes <= 2: - return "" + if bytes <= 2: + return "" - result = $buffer_user + result = $buffer_user - WTSFreeMemory(buffer_user) + WTSFreeMemory(buffer_user) proc getAddressForSession(server: HANDLE, sessionId: DWORD): string = - var bytes: DWORD = 0 - var buffer_addr: LPWSTR = NULL - if WTSQuerySessionInformationW(server, sessionId, WTS_INFO_CLASS.WTSClientAddress, addr buffer_addr, addr bytes) == 0: - raiseError() + var bytes: DWORD = 0 + var buffer_addr: LPWSTR = NULL + if WTSQuerySessionInformationW(server, sessionId, + WTS_INFO_CLASS.WTSClientAddress, addr buffer_addr, addr bytes) == 0: + raiseError() - let address = cast[PWTS_CLIENT_ADDRESS](buffer_addr).address - let addressFamily = cast[PWTS_CLIENT_ADDRESS](buffer_addr).addressFamily + let address = cast[PWTS_CLIENT_ADDRESS](buffer_addr).address + let addressFamily = cast[PWTS_CLIENT_ADDRESS](buffer_addr).addressFamily - if addressFamily == 0: - result = &"{address[0]}.{address[1]}.{address[2]}.{address[3]}" + if addressFamily == 0: + result = &"{address[0]}.{address[1]}.{address[2]}.{address[3]}" - WTSFreeMemory(buffer_addr) + WTSFreeMemory(buffer_addr) proc getLoginTimeForSession(server: HANDLE, sessionId: DWORD): float = - var station_info: WINSTATION_INFO - var returnLen: ULONG - if WinStationQueryInformation(server, sessionId, WinStationInformation, addr station_info, sizeof(station_info).ULONG, addr returnLen) == 0: - return -1 + var station_info: WINSTATION_INFO + var returnLen: ULONG + if WinStationQueryInformation(server, sessionId, WinStationInformation, + addr station_info, sizeof(station_info).ULONG, addr returnLen) == 0: + return -1 - result = toUnixTime(station_info.ConnectTime) + result = toUnixTime(station_info.ConnectTime) proc users*(): seq[User] = - var count: DWORD = 0 - var sessions: PWTS_SESSION_INFO - if WTSEnumerateSessionsW(WTS_CURRENT_SERVER_HANDLE, 0, 1, addr sessions, addr count) == 0: - raiseError() + var count: DWORD = 0 + var sessions: PWTS_SESSION_INFO + if WTSEnumerateSessionsW(WTS_CURRENT_SERVER_HANDLE, 0, 1, addr sessions, + addr count) == 0: + raiseError() - for i in 0 ..< count: - let currentSession = cast[PWTS_SESSION_INFO](cast[int](sessions) + (sizeof(WTS_SESSION_INFO)*i)) - let sessionId = currentSession.sessionId + for i in 0 ..< count: + let currentSession = cast[PWTS_SESSION_INFO](cast[int](sessions) + (sizeof( + WTS_SESSION_INFO)*i)) + let sessionId = currentSession.sessionId - let user = getUserForSession(WTS_CURRENT_SERVER_HANDLE, sessionId) - if user == "": continue + let user = getUserForSession(WTS_CURRENT_SERVER_HANDLE, sessionId) + if user == "": continue - let address = getAddressForSession(WTS_CURRENT_SERVER_HANDLE, sessionId) - let login_time = getLoginTimeForSession(WTS_CURRENT_SERVER_HANDLE, sessionId) + let address = getAddressForSession(WTS_CURRENT_SERVER_HANDLE, sessionId) + let login_time = getLoginTimeForSession(WTS_CURRENT_SERVER_HANDLE, sessionId) - result.add(User(name:user, host:address, started:login_time)) + result.add(User(name: user, host: address, started: login_time)) - WTSFreeMemory(sessions) + WTSFreeMemory(sessions) ## ToDo - These are all stubbed out so things compile. ## It also shows what needs to be done for feature parity with Linux -proc cpu_stats*(): tuple[ctx_switches, interrupts, soft_interrupts, syscalls: int] = - raise newException( Exception, "Function is unimplemented!") +proc cpu_stats*(): tuple[ctx_switches, interrupts, soft_interrupts, + syscalls: int] = + raise newException(Exception, "Function is unimplemented!") -proc net_connections*( kind= "inet", pid= -1 ): seq[Connection] = - raise newException( Exception, "Function is unimplemented!") +proc net_connections*(kind = "inet", pid = -1): seq[Connection] = + raise newException(Exception, "Function is unimplemented!") proc net_if_addrs*(): Table[string, seq[common.Address]] = - raise newException( Exception, "Function is unimplemented!") + raise newException(Exception, "Function is unimplemented!") proc net_if_stats*(): TableRef[string, NICstats] = - raise newException( Exception, "Function is unimplemented!") + raise newException(Exception, "Function is unimplemented!") proc per_disk_io_counters*(): TableRef[string, DiskIO] = - raise newException( Exception, "Function is unimplemented!") + raise newException(Exception, "Function is unimplemented!") proc per_nic_net_io_counters*(): TableRef[string, NetIO] = - raise newException( Exception, "Function is unimplemented!") + raise newException(Exception, "Function is unimplemented!") proc process_exists*(processName: string): bool = - var exists = false - var entry: PROCESSENTRY32 - entry.dwSize = cast[DWORD](PROCESSENTRY32.sizeof) + var exists = false + var entry: PROCESSENTRY32 + entry.dwSize = cast[DWORD](PROCESSENTRY32.sizeof) - var snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) + var snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) - if Process32First(snapshot, entry.addr): - while Process32Next(snapshot, entry.addr): - var name: string - for c in entry.szExeFile: - if cast[char](c) == '\0': - break + if Process32First(snapshot, entry.addr): + while Process32Next(snapshot, entry.addr): + var name: string + for c in entry.szExeFile: + if cast[char](c) == '\0': + break - name.add(cast[char](c)) + name.add(cast[char](c)) - if name == processName: - exists = true + if name == processName: + exists = true - CloseHandle(snapshot) - return exists + CloseHandle(snapshot) + return exists proc pid_exists*(pid: int): bool = - var p = OpenProcess(SYNCHRONIZE, FALSE, cast[DWORD](pid)); - var r = WaitForSingleObject(p, 0); - CloseHandle(p); - return r == WAIT_TIMEOUT + var p = OpenProcess(SYNCHRONIZE, FALSE, cast[DWORD](pid)); + var r = WaitForSingleObject(p, 0); + CloseHandle(p); + return r == WAIT_TIMEOUT proc pid_cmdline*(pid: int): string = - raise newException( Exception, "Function is unimplemented!") + raise newException(Exception, "Function is unimplemented!") diff --git a/tests/quick_test.nim b/tests/quick_test.nim index 7aff0b3..d91aad7 100644 --- a/tests/quick_test.nim +++ b/tests/quick_test.nim @@ -13,17 +13,17 @@ echo_proc per_cpu_times() echo_proc cpu_stats() echo_proc pid_exists(1) echo_proc pid_exists(999) -echo_proc cpu_count(logical=true) -echo_proc cpu_count(logical=false) -echo_proc cpu_percent( interval=0.0 ) -echo_proc cpu_percent( interval=1.0 ) -echo_proc per_cpu_percent( interval=0.0 ) -echo_proc per_cpu_percent( interval=1.0 ) +echo_proc cpu_count(logical = true) +echo_proc cpu_count(logical = false) +echo_proc cpu_percent(interval = 0.0) +echo_proc cpu_percent(interval = 1.0) +echo_proc per_cpu_percent(interval = 0.0) +echo_proc per_cpu_percent(interval = 1.0) echo_proc disk_usage(".") echo_proc virtual_memory() echo_proc swap_memory() -echo_proc disk_partitions( all=false ) -echo_proc disk_partitions( all=true ) +echo_proc disk_partitions(all = false) +echo_proc disk_partitions(all = true) echo_proc net_io_counters() echo_proc per_nic_net_io_counters() echo_proc net_if_stats() diff --git a/tests/test_linux.nim b/tests/test_linux.nim index 993d781..2a3c8e9 100644 --- a/tests/test_linux.nim +++ b/tests/test_linux.nim @@ -6,19 +6,19 @@ import ../src/psutil template echo_proc(x: untyped) = echo "\n\n", astToStr(x), "\n", x - + proc vmstat(stat: string): int = - for line in splitlines( execProcess("vmstat -s") ): - if stat in line: - return parseInt(line.splitWhitespace()[0]) - raise newException( ValueError, "can't find $1 in 'vmstat' output" % stat) + for line in splitlines(execProcess("vmstat -s")): + if stat in line: + return parseInt(line.splitWhitespace()[0]) + raise newException(ValueError, "can't find $1 in 'vmstat' output" % stat) test "test boot time": - let vmstat_value = vmstat("boot time") - let psutil_value = psutil.boot_time() - check( vmstat_value == int(psutil_value) ) + let vmstat_value = vmstat("boot time") + let psutil_value = psutil.boot_time() + check(vmstat_value == int(psutil_value)) diff --git a/tests/test_windows.nim b/tests/test_windows.nim index 5150d48..5c61145 100644 --- a/tests/test_windows.nim +++ b/tests/test_windows.nim @@ -2,7 +2,7 @@ import ../src/psutil import strformat template echo_proc(x: untyped) = echo "\n\n", astToStr(x), "\n", x - + when defined(windows): echo fmt"{getnativearch()}" # echo_proc pid_exists(77724) @@ -19,4 +19,4 @@ when defined(windows): echo_proc pid_paths(pids()) echo_proc pid_parent(pids()[0]) echo_proc psutil_get_drive_type("C:\\") - echo_proc process_exists("test_windows.exe") \ No newline at end of file + echo_proc process_exists("test_windows.exe")