Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enhanced support for Mac OS #142

Merged
merged 2 commits into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/catnaplib/global/definitions.nim
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ const
"dragonfly": "pkg",
"netbsd": "pkgsrc",
"openbsd": "pkgsrc",
"macos": "homebrew",
}.toOrderedTable
PKGCOUNTCOMMANDS* = static: {
"apx": "apx list -i | wc -l",
Expand All @@ -136,6 +137,7 @@ const
"apk": "apk list --installed | wc -l",
"pkg": "pkg info",
"pkgsrc": "pkg_info",
"homebrew": "brew list | wc -l",
}.toOrderedTable

# Files / Dirs
Expand Down
38 changes: 21 additions & 17 deletions src/catnaplib/platform/fetch.nim
Original file line number Diff line number Diff line change
Expand Up @@ -19,31 +19,35 @@ proc fetchSystemInfo*(config: Config, distroId: string = "nil"): FetchInfo =
result.list["terminal"] = proc(): string = return probe.getTerminal()
result.list["shell"] = proc(): string = return probe.getShell()
result.list["memory"] = proc(): string = return probe.getMemory()
result.list["battery"] = proc(): string = return probe.getBattery()
result.list["battery"] = proc(): string = return probe.getBattery()
result.list["cpu"] = proc(): string = return probe.getCpu()
result.list["gpu"] = proc(): string = return probe.getGpu()
result.list["packages"] = proc(): string = return probe.getPackages()
result.list["weather"] = proc(): string = return probe.getWeather()
if defined(linux):
# Add a disk stat for all mounts
let mounts: seq[string] = probe.getMounts()

# Add a disk stat for all mounts
let mounts: seq[string] = probe.getMounts()
if mounts.len > 1:
var index = 0
for mount in mounts:
let name = "disk_" & $index

if mounts.len > 1:
var index = 0
for mount in mounts:
let name = "disk_" & $index
# Capture mount var to prevent value changes
var cap: proc(): string
capture mount:
# Create fetch proc
cap = proc(): string = return probe.getDisk(mount)

# Capture mount var to prevent value changes
var cap: proc(): string
capture mount:
# Create fetch proc
cap = proc(): string = return probe.getDisk(mount)

result.list[name] = cap
result.disk_statnames.add(name)
index += 1
result.list[name] = cap
result.disk_statnames.add(name)
index += 1
else:
result.list["disk_0"] = proc(): string = return probe.getDisk(mounts[0])
result.disk_statnames.add("disk_0")
else:
result.list["disk_0"] = proc(): string = return probe.getDisk(mounts[0])
#TODO: add macos support
result.list["disk_0"] = proc(): string = ""
result.disk_statnames.add("disk_0")

var distroId = (if distroId != "nil": distroId else: result.distroId.id)
Expand Down
191 changes: 115 additions & 76 deletions src/catnaplib/platform/probe.nim
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@ proc getDistro*(): string =
when defined(linux) or defined(bsd):
# Returns the name of the running linux distro
result = "/etc/os-release".loadConfig.getSectionValue("", "PRETTY_NAME") & " " & uname().machine
elif defined(macos):
elif defined(macosx):
result = "MacOS X" & " " & uname().machine
else:
result = "Unknown"

writeCache(cacheFile, result, INFINITEDURATION)

Expand All @@ -36,24 +38,35 @@ proc getDistroId*(): DistroId =
result.id = raw_vals[0]
result.like = raw_vals[1]


if fileExists("/boot/issue.txt"): # Check if raspbian else get distroid from /etc/os-release
result.id = "raspbian"
result.like = "debian"
if defined(linux):
if fileExists("/boot/issue.txt"): # Check if raspbian else get distroid from /etc/os-release
result.id = "raspbian"
result.like = "debian"
else:
result.id = "/etc/os-release".loadConfig.getSectionValue("", "ID").toLower()
result.like = "/etc/os-release".loadConfig.getSectionValue("", "ID_LIKE").toLower()
elif defined(macosx):
result.id = "macos"
result.like = "macos"
else:
result.id = "/etc/os-release".loadConfig.getSectionValue("", "ID").toLower()
result.like = "/etc/os-release".loadConfig.getSectionValue("", "ID_LIKE").toLower()

result.id = "Unknown"
result.like = "Unknown"
writeCache(cacheFile, &"{result.id}|{result.like}", INFINITEDURATION)

proc getUptime*(): string =
# Returns the system uptime as a string (DAYS, HOURS, MINUTES)

# Uptime in sec
let uptime = "/proc/uptime".open.readLine.split(".")[0]
var utu: int
if defined(linux):
let uptime = "/proc/uptime".open.readLine.split(".")[0]
utu = uptime.parseInt
else:
let boottime = execProcess("sysctl -n kern.boottime").split(" ")[3].split(",")[0]
let now = epochTime()
utu = toInt(now) - parseInt(boottime)

let
utu = uptime.parseUInt
uth = utu div 3600 mod 24 # hours
utm = utu mod 3600 div 60 # minutes
utd = utu div 3600 div 24 # days
Expand Down Expand Up @@ -96,13 +109,22 @@ proc getProcessName(pid: int): string =

proc getTerminal*(): string =
# Returns the currently running terminal emulator
result = getCurrentProcessID().getParentPid().getParentPid().getProcessName()
if result == "login" or result == "sshd":
result = "tty"
if defined(linux):
result = getCurrentProcessID().getParentPid().getParentPid().getProcessName()
if result == "login" or result == "sshd":
result = "tty"
elif defined(macosx):
result = getEnv("TERM_PROGRAM")
else:
result = "Unknown"

proc getShell*(): string =
# Returns the system shell
result = getCurrentProcessID().getParentPid().getProcessName()
if defined(linux):
result = getCurrentProcessID().getParentPid().getProcessName()
else:
#TODO: add macos support
result = ""

proc getDesktop*(): string =
# Returns the running desktop env
Expand All @@ -127,49 +149,61 @@ proc getDesktop*(): string =

proc getMemory*(mb: bool = true): string =
# Returns statistics about the memory
let
fileSeq: seq[string] = "/proc/meminfo".readLines(3)
if defined(linux):
let
fileSeq: seq[string] = "/proc/meminfo".readLines(3)

dividend: uint = if mb: 1000 else: 1024
suffix: string = if mb: "MB" else: "MiB"
dividend: uint = if mb: 1000 else: 1024
suffix: string = if mb: "MB" else: "MiB"

memTotalString = fileSeq[0].split(" ")[^2]
memAvailableString = fileSeq[2].split(" ")[^2]
memTotalString = fileSeq[0].split(" ")[^2]
memAvailableString = fileSeq[2].split(" ")[^2]

memTotalInt = memTotalString.parseUInt div dividend
memAvailableInt = memAvailableString.parseUInt div dividend
memTotalInt = memTotalString.parseUInt div dividend
memAvailableInt = memAvailableString.parseUInt div dividend

memUsedInt = memTotalInt - memAvailableInt
percentage = ((int(memUsedInt) / int(memTotalInt)) * 100).round().int()
memUsedInt = memTotalInt - memAvailableInt
percentage = ((int(memUsedInt) / int(memTotalInt)) * 100).round().int()

result = &"{memUsedInt} / {memTotalInt} {suffix} ({percentage}%)"
result = &"{memUsedInt} / {memTotalInt} {suffix} ({percentage}%)"
else:
#TODO: add macos support
result = ""

proc getBattery*(): string =
# Credits to https://gitlab.com/prashere/battinfo for regex implementation.

let
BATTERY_REGEX = re"^BAT\d+$"
powerPath = "/sys/class/power_supply/"
if defined(linux):
# Credits to https://gitlab.com/prashere/battinfo for regex implementation.
let
BATTERY_REGEX = re"^BAT\d+$"
powerPath = "/sys/class/power_supply/"

var batterys: seq[tuple[idx: int, path: string]]
var batterys: seq[tuple[idx: int, path: string]]

# Collect all batterys
for dir in os.walk_dir(powerPath):
if re.match(os.last_path_part(dir.path), BATTERY_REGEX):
batterys.add((parseInt($dir.path[^1]), dir.path & "/"))
# Collect all batterys
for dir in os.walk_dir(powerPath):
if re.match(os.last_path_part(dir.path), BATTERY_REGEX):
batterys.add((parseInt($dir.path[^1]), dir.path & "/"))

if batterys.len < 1:
logError("No battery detected!")
if batterys.len < 1:
logError("No battery detected!")

# Sort batterys by number
sort(batterys)
# Sort batterys by number
sort(batterys)

# Get stats for battery with lowest number
let
batteryCapacity = readFile(batterys[0].path & "capacity").strip()
batteryStatus = readFile(batterys[0].path & "status").strip()
# Get stats for battery with lowest number
let
batteryCapacity = readFile(batterys[0].path & "capacity").strip()
batteryStatus = readFile(batterys[0].path & "status").strip()

result = &"{batteryCapacity}% ({batteryStatus})"
result = &"{batteryCapacity}% ({batteryStatus})"
elif defined(macosx):
let
pmset = execProcess("pmset -g batt | tail -n 1").split("\t")[1].split("; ")
batteryCapacity = pmset[0]
batteryStatus = pmset[1]
result = &"{batteryCapacity} ({batteryStatus})"
else:
result = "Unknown"

proc getMounts*(): seq[string] =
proc getMountPoints(): cstring {.importc, varargs, header: "getDisk.h".}
Expand Down Expand Up @@ -213,26 +247,28 @@ proc getCpu*(): string =
if result != "":
return

when defined(macos):
return execCmd("sysctl -n machdep.cpu.brand_string")

let rawLines = readFile("/proc/cpuinfo").split("\n")

var key_name = "model name"
if getDistroId().id == "raspbian": key_name = "Model"

for rawLine in rawLines:
let line = rawLine.split(":")

if line.len < 2: continue
if defined(linux):
let rawLines = readFile("/proc/cpuinfo").split("\n")

var key_name = "model name"
if getDistroId().id == "raspbian": key_name = "Model"

for rawLine in rawLines:
let line = rawLine.split(":")

if line.len < 2: continue

let
key = line[0].strip()
val = line[1].strip()
if key == key_name:
result = val
break
elif defined(macosx):
result = execProcess("sysctl -n machdep.cpu.brand_string").split("\n")[0]
else:
result = "Unknown"

let
key = line[0].strip()
val = line[1].strip()
if key == key_name:
result = val
break

writeCache(cacheFile, result, initDuration(days=1))

proc getPkgManager(distroId: DistroId): string =
Expand All @@ -243,7 +279,7 @@ proc getPkgManager(distroId: DistroId): string =
for key in PKGMANAGERS.keys:
if distroId.like == key:
return PKGMANAGERS[key]

return "Unknown"

proc getPackages*(distroId: DistroId = getDistroId()): string =
Expand Down Expand Up @@ -283,22 +319,25 @@ proc getGpu*(): string =
if result != "":
return

let tmpFile = "lspci.txt".toTmpPath
if defined(linux):
let tmpFile = "lspci.txt".toTmpPath

if execCmd("lspci > " & tmpFile) != 0:
logError("Failed to fetch GPU!")
if execCmd("lspci > " & tmpFile) != 0:
logError("Failed to fetch GPU!")

var vga = "Unknown"
let lspci = readFile(tmpFile)
for line in lspci.split('\n'):
if line.split(' ')[1] == "VGA":
vga = line
break
var vga = "Unknown"
let lspci = readFile(tmpFile)
for line in lspci.split('\n'):
if line.split(' ')[1] == "VGA":
vga = line
break

let vga_parts = vga.split(":")
let vga_parts = vga.split(":")

if vga_parts.len >= 2 or vga != "Unknown":
result = vga_parts[vga_parts.len - 1].split("(")[0].strip()
if vga_parts.len >= 2 or vga != "Unknown":
result = vga_parts[vga_parts.len - 1].split("(")[0].strip()
elif defined(macosx):
result = execProcess("system_profiler SPDisplaysDataType | grep 'Chipset Model'").split(": ")[1].split("\n")[0]
else:
result = "Unknown"

Expand Down