diff --git a/dashboard/src/lib/components/DeviceIcon.svelte b/dashboard/src/lib/components/DeviceIcon.svelte index 774be2f9..ac980e74 100644 --- a/dashboard/src/lib/components/DeviceIcon.svelte +++ b/dashboard/src/lib/components/DeviceIcon.svelte @@ -9,7 +9,7 @@ */ interface Props { - /** "macbook pro" | "mac studio" | "mac mini" etc. */ + /** "macbook pro" | "mac studio" | "mac mini" | "dgx spark" | "linux" etc. */ deviceType: string; /** Center X coordinate in SVG space */ cx: number; @@ -38,10 +38,50 @@ const LOGO_NATIVE_WIDTH = 814; const LOGO_NATIVE_HEIGHT = 1000; + // NVIDIA logo SVG path + const NVIDIA_LOGO_PATH = + "M0.81 0.429V0.299c0.013 -0.001 0.026 -0.002 0.038 -0.002 0.355 -0.011 0.588 0.306 0.588 0.306S1.186 0.952 0.916 0.952c-0.036 0 -0.071 -0.006 -0.105 -0.017V0.542c0.138 0.017 0.166 0.078 0.249 0.216l0.185 -0.155s-0.135 -0.177 -0.362 -0.177c-0.024 -0.001 -0.048 0.001 -0.072 0.003m0 -0.429v0.194l0.038 -0.002c0.494 -0.017 0.816 0.405 0.816 0.405s-0.37 0.45 -0.754 0.45c-0.034 0 -0.066 -0.003 -0.099 -0.009v0.12c0.027 0.003 0.055 0.006 0.082 0.006 0.358 0 0.618 -0.183 0.869 -0.399 0.042 0.034 0.212 0.114 0.247 0.15 -0.238 0.2 -0.794 0.361 -1.11 0.361 -0.03 0 -0.059 -0.002 -0.088 -0.005v0.169h1.362V0zm0 0.935v0.102c-0.331 -0.059 -0.423 -0.404 -0.423 -0.404s0.159 -0.176 0.423 -0.205v0.112h-0.001C0.671 0.524 0.562 0.654 0.562 0.654s0.062 0.218 0.248 0.282m-0.588 -0.316s0.196 -0.29 0.589 -0.32V0.194C0.376 0.229 0 0.597 0 0.597s0.213 0.616 0.81 0.672v-0.112c-0.438 -0.054 -0.588 -0.538 -0.588 -0.538"; + + // Tux penguin paths (viewBox 0 0 100 100) + const TUX_BODY_PATH = + "M50 8c-8 0-14 6-14 13 0 4 2 8 5 10-8 4-16 14-16 28v12c0 4 2 7 5 9l-6 4c-2 1-3 3-3 5v3c0 2 2 4 4 4h10l4-4h22l4 4h10c2 0 4-2 4-4v-3c0-2-1-4-3-5l-6-4c3-2 5-5 5-9V59c0-14-8-24-16-28 3-2 5-6 5-10 0-7-6-13-14-13z"; + const TUX_BELLY_PATH = + "M38 52c0-8 5-15 12-15s12 7 12 15v14c0 4-5 7-12 7s-12-3-12-7V52z"; + const wireColor = "rgba(179,179,179,0.8)"; const strokeWidth = 1.5; const modelLower = $derived(deviceType.toLowerCase()); + const isSpark = $derived( + modelLower.includes("dgx") || modelLower.includes("gx10"), + ); + const isLinux = $derived(!isSpark && modelLower.includes("linux")); + + // ── DGX Spark dimensions ── + const dgxW = $derived(size * 1.55); + const dgxH = $derived(size * 0.58); + const dgxX = $derived(cx - dgxW / 2); + const dgxY = $derived(cy - dgxH / 2); + const dgxChassisX = $derived(dgxX - dgxW * 0.03); + const dgxChassisW = $derived(dgxW * 1.05); + const dgxHandleW = $derived(dgxW * 0.27); + const dgxHandleGap = $derived(dgxH * 0.05); + const dgxHandleH = $derived(dgxH - dgxHandleGap * 2); + const dgxHandleY = $derived(dgxY + dgxHandleGap); + const dgxInnerHandleW = $derived(dgxW * 0.12); + const dgxInnerHandleH = $derived(dgxHandleH - dgxH * 0.06); + const dgxLeftHandleX = $derived(dgxX + 4); + const dgxRightHandleX = $derived(dgxX + dgxW - dgxHandleW - 4); + const dgxClipId = $derived(`di-dgx-${uid}`); + const dgxTextureId = $derived(`di-dgx-tex-${uid}`); + + // ── Linux Tux dimensions ── + const tuxSize = $derived(Math.min(size * 1.0, size * 1.1)); + const tuxScale = $derived(tuxSize / 100); + const tuxTx = $derived(cx - (100 * tuxScale) / 2); + const tuxTy = $derived(cy - (100 * tuxScale) / 2); + const tuxEyeR = $derived(2.5 * tuxScale); + const tuxBellyClipId = $derived(`di-tux-belly-${uid}`); // ── Mac Studio dimensions (same ratios as TopologyGraph) ── const studioW = $derived(size * 1.25); @@ -114,7 +154,230 @@ const studioClipId = $derived(`di-studio-${uid}`); -{#if modelLower === "mac studio" || modelLower === "mac mini"} +{#if isSpark} + + + + + + + + + + + + + + + + + + + + + {#if ramPercent > 0} + + {/if} + + + + + + + + + + + {@const badgeW = dgxW * 0.09} + {@const badgeH = dgxHandleH * 0.5} + {@const badgeX = dgxLeftHandleX + dgxHandleW - badgeW - dgxHandleW * 0.06} + {@const badgeYPos = dgxHandleY + (dgxHandleH - badgeH) / 2} + {@const textSz = badgeW * 0.58} + {@const logoW = textSz * 1.2} + {@const logoH = logoW * (1.438 / 2.174)} + {@const ctrX = badgeX + badgeW / 2 - badgeW * 0.03} + {@const ctrY = badgeYPos + badgeH / 2} + {@const labelGap = badgeW * 0.15} + {@const totalW = logoW + labelGap + textSz * 3.6} + + + + + NVIDIA + +{:else if isLinux} + + + + + + + + + + + + + + + {#if ramPercent > 0} + {@const bellyTop = tuxTy + 37 * tuxScale} + {@const bellyH = 36 * tuxScale} + {@const memH = (ramPercent / 100) * bellyH} + + {/if} + + + + + + + + + + + + + + +{:else if modelLower === "mac studio" || modelLower === "mac mini"} diff --git a/dashboard/src/lib/components/ModelCard.svelte b/dashboard/src/lib/components/ModelCard.svelte index b432b7a8..ce3449d3 100644 --- a/dashboard/src/lib/components/ModelCard.svelte +++ b/dashboard/src/lib/components/ModelCard.svelte @@ -169,8 +169,10 @@ function getDeviceType( name: string, - ): "macbook" | "studio" | "mini" | "unknown" { + ): "macbook" | "studio" | "mini" | "dgx" | "linux" | "unknown" { const lower = name.toLowerCase(); + if (lower.includes("dgx") || lower.includes("gx10")) return "dgx"; + if (lower.includes("linux")) return "linux"; if (lower.includes("macbook")) return "macbook"; if (lower.includes("studio")) return "studio"; if (lower.includes("mini")) return "mini"; diff --git a/dashboard/src/lib/components/TopologyGraph.svelte b/dashboard/src/lib/components/TopologyGraph.svelte index 0d0d3c3d..cc9823bf 100644 --- a/dashboard/src/lib/components/TopologyGraph.svelte +++ b/dashboard/src/lib/components/TopologyGraph.svelte @@ -117,6 +117,17 @@ const LOGO_NATIVE_WIDTH = 814; const LOGO_NATIVE_HEIGHT = 1000; + // NVIDIA logo SVG path (from exo-nvidia) + const NVIDIA_LOGO_PATH = + "M0.81 0.429V0.299c0.013 -0.001 0.026 -0.002 0.038 -0.002 0.355 -0.011 0.588 0.306 0.588 0.306S1.186 0.952 0.916 0.952c-0.036 0 -0.071 -0.006 -0.105 -0.017V0.542c0.138 0.017 0.166 0.078 0.249 0.216l0.185 -0.155s-0.135 -0.177 -0.362 -0.177c-0.024 -0.001 -0.048 0.001 -0.072 0.003m0 -0.429v0.194l0.038 -0.002c0.494 -0.017 0.816 0.405 0.816 0.405s-0.37 0.45 -0.754 0.45c-0.034 0 -0.066 -0.003 -0.099 -0.009v0.12c0.027 0.003 0.055 0.006 0.082 0.006 0.358 0 0.618 -0.183 0.869 -0.399 0.042 0.034 0.212 0.114 0.247 0.15 -0.238 0.2 -0.794 0.361 -1.11 0.361 -0.03 0 -0.059 -0.002 -0.088 -0.005v0.169h1.362V0zm0 0.935v0.102c-0.331 -0.059 -0.423 -0.404 -0.423 -0.404s0.159 -0.176 0.423 -0.205v0.112h-0.001C0.671 0.524 0.562 0.654 0.562 0.654s0.062 0.218 0.248 0.282m-0.588 -0.316s0.196 -0.29 0.589 -0.32V0.194C0.376 0.229 0 0.597 0 0.597s0.213 0.616 0.81 0.672v-0.112c-0.438 -0.054 -0.588 -0.538 -0.588 -0.538"; + + // Tux penguin SVG path — simplified silhouette for topology view + // viewBox: 0 0 100 100 + const TUX_BODY_PATH = + "M50 8c-8 0-14 6-14 13 0 4 2 8 5 10-8 4-16 14-16 28v12c0 4 2 7 5 9l-6 4c-2 1-3 3-3 5v3c0 2 2 4 4 4h10l4-4h22l4 4h10c2 0 4-2 4-4v-3c0-2-1-4-3-5l-6-4c3-2 5-5 5-9V59c0-14-8-24-16-28 3-2 5-6 5-10 0-7-6-13-14-13z"; + const TUX_BELLY_PATH = + "M38 52c0-8 5-15 12-15s12 7 12 15v14c0 4-5 7-12 7s-12-3-12-7V52z"; + function formatBytes(bytes: number, decimals = 1): string { if (!bytes || bytes === 0) return "0B"; const k = 1024; @@ -554,6 +565,12 @@ const clipPathId = `clip-${nodeInfo.id.replace(/[^a-zA-Z0-9]/g, "-")}`; const modelLower = modelId.toLowerCase(); + const identity = identitiesData[nodeInfo.id]; + const nameLower = (friendlyName || "").toLowerCase(); + const isSpark = + modelLower.includes("dgx") || modelLower.includes("gx10"); + const isLinux = + !isSpark && (modelLower === "linux" || identity?.osVersion === "Linux"); // Check node states for styling const isHighlighted = highlightedNodes.has(nodeInfo.id); @@ -623,7 +640,294 @@ `${friendlyName}\nID: ${nodeInfo.id.slice(-8)}\nMemory: ${formatBytes(ramUsed)}/${formatBytes(ramTotal)}`, ); - if (modelLower === "mac studio") { + if (isSpark) { + // NVIDIA DGX Spark — gold chassis with textured front, side handles, and NVIDIA badge + iconBaseWidth = nodeRadius * 1.55; + iconBaseHeight = nodeRadius * 0.58; + const x = nodeInfo.x - iconBaseWidth / 2; + const y = nodeInfo.y - iconBaseHeight / 2; + const chassisX = x - iconBaseWidth * 0.03; + const chassisWidth = iconBaseWidth * 1.05; + const cornerRadius = 3; + + const dgxClipId = `dgx-clip-${nodeInfo.id.replace(/[^a-zA-Z0-9]/g, "-")}`; + defs + .append("clipPath") + .attr("id", dgxClipId) + .append("rect") + .attr("x", x) + .attr("y", y) + .attr("width", iconBaseWidth) + .attr("height", iconBaseHeight) + .attr("rx", cornerRadius); + + // Chassis texture pattern + const textureId = `chassis-texture-${nodeInfo.id.replace(/[^a-zA-Z0-9]/g, "-")}`; + defs + .append("pattern") + .attr("id", textureId) + .attr("patternUnits", "userSpaceOnUse") + .attr("width", 8) + .attr("height", 8); + const texturePattern = defs.select(`#${textureId}`); + texturePattern + .append("rect") + .attr("width", 8) + .attr("height", 8) + .attr("fill", "#6f6248"); + texturePattern + .append("circle") + .attr("cx", 2) + .attr("cy", 2) + .attr("r", 1) + .attr("fill", "#5a4f3b") + .attr("opacity", 0.5); + texturePattern + .append("circle") + .attr("cx", 6) + .attr("cy", 6) + .attr("r", 1) + .attr("fill", "#4a4232") + .attr("opacity", 0.45); + + // Main body + nodeG + .append("rect") + .attr("class", "node-outline") + .attr("x", chassisX) + .attr("y", y) + .attr("width", chassisWidth) + .attr("height", iconBaseHeight) + .attr("rx", cornerRadius) + .attr("fill", `url(#${textureId})`) + .attr("stroke", wireColor) + .attr("stroke-width", strokeWidth); + + // Side border accents + const sideThickness = iconBaseWidth * 0.02; + nodeG + .append("rect") + .attr("x", chassisX) + .attr("y", y) + .attr("width", sideThickness) + .attr("height", iconBaseHeight) + .attr("fill", "#8a7a56"); + nodeG + .append("rect") + .attr("x", chassisX + chassisWidth - sideThickness) + .attr("y", y) + .attr("width", sideThickness) + .attr("height", iconBaseHeight) + .attr("fill", "#8a7a56"); + + // Memory fill (bottom up) + if (ramUsagePercent > 0) { + const memFillHeight = (ramUsagePercent / 100) * iconBaseHeight; + nodeG + .append("rect") + .attr("x", x) + .attr("y", y + iconBaseHeight - memFillHeight) + .attr("width", iconBaseWidth) + .attr("height", memFillHeight) + .attr("fill", "rgba(255,215,0,0.45)") + .attr("clip-path", `url(#${dgxClipId})`); + } + + // Side handles with inner recess + const handleWidth = iconBaseWidth * 0.27; + const handleGap = iconBaseHeight * 0.05; + const handleHeight = iconBaseHeight - handleGap * 2; + const handleY = y + handleGap; + const innerHandleWidth = iconBaseWidth * 0.12; + const innerHandleHeight = handleHeight - iconBaseHeight * 0.06; + const leftHandleX = x + 4; + const rightHandleX = x + iconBaseWidth - handleWidth - 4; + + // Left handle + nodeG + .append("rect") + .attr("x", leftHandleX) + .attr("y", handleY) + .attr("width", handleWidth) + .attr("height", handleHeight) + .attr("rx", 2.4) + .attr("fill", "#b3a170") + .attr("stroke", "#403723") + .attr("stroke-width", 0.7); + nodeG + .append("rect") + .attr("x", leftHandleX + handleWidth * 0.06) + .attr("y", handleY + iconBaseHeight * 0.03) + .attr("width", innerHandleWidth) + .attr("height", innerHandleHeight) + .attr("rx", 1.6) + .attr("fill", "#8a7a56"); + + // Right handle + nodeG + .append("rect") + .attr("x", rightHandleX) + .attr("y", handleY) + .attr("width", handleWidth) + .attr("height", handleHeight) + .attr("rx", 2.4) + .attr("fill", "#b3a170") + .attr("stroke", "#403723") + .attr("stroke-width", 0.7); + nodeG + .append("rect") + .attr( + "x", + rightHandleX + handleWidth - innerHandleWidth - handleWidth * 0.08, + ) + .attr("y", handleY + iconBaseHeight * 0.03) + .attr("width", innerHandleWidth) + .attr("height", innerHandleHeight) + .attr("rx", 1.6) + .attr("fill", "#8a7a56"); + + // NVIDIA logo + text label (rotated 90 deg on left handle) + const badgeWidth = iconBaseWidth * 0.09; + const badgeHeight = handleHeight * 0.5; + const badgeX = + leftHandleX + handleWidth - badgeWidth - handleWidth * 0.06; + const badgeY = handleY + (handleHeight - badgeHeight) / 2; + const textSize = badgeWidth * 0.58; + const logoWidth = textSize * 1.2; + const logoHeight = logoWidth * (1.438 / 2.174); + const centerX = badgeX + badgeWidth / 2 - badgeWidth * 0.03; + const centerY = badgeY + badgeHeight / 2; + const gap = badgeWidth * 0.15; + const totalWidth = logoWidth + gap + textSize * 3.6; + + const labelGroup = nodeG + .append("g") + .attr("transform", `rotate(90 ${centerX} ${centerY})`); + + labelGroup + .append("svg") + .attr("x", centerX - totalWidth / 2) + .attr("y", centerY - logoHeight / 2) + .attr("width", logoWidth) + .attr("height", logoHeight) + .attr("viewBox", "0 0 2.174 1.438") + .append("path") + .attr("d", NVIDIA_LOGO_PATH) + .attr("fill", "#76b900"); + + labelGroup + .append("text") + .attr("x", centerX - totalWidth / 2 + logoWidth + gap) + .attr("y", centerY) + .attr("text-anchor", "start") + .attr("dominant-baseline", "middle") + .attr("fill", "#8a7a56") + .attr("font-size", textSize) + .attr("font-family", "monospace") + .attr("font-weight", "700") + .text("NVIDIA"); + } else if (isLinux) { + // Linux — Tux penguin + iconBaseWidth = nodeRadius * 1.0; + iconBaseHeight = nodeRadius * 1.1; + const tuxSize = Math.min(iconBaseWidth, iconBaseHeight); + const scale = tuxSize / 100; + const tx = nodeInfo.x - (100 * scale) / 2; + const ty = nodeInfo.y - (100 * scale) / 2; + + // Body (dark outline) + nodeG + .append("path") + .attr("class", "node-outline") + .attr("d", TUX_BODY_PATH) + .attr("transform", `translate(${tx}, ${ty}) scale(${scale})`) + .attr("fill", "#1a1a1a") + .attr("stroke", wireColor) + .attr("stroke-width", strokeWidth / scale); + + // White belly + nodeG + .append("path") + .attr("d", TUX_BELLY_PATH) + .attr("transform", `translate(${tx}, ${ty}) scale(${scale})`) + .attr("fill", "rgba(220,220,220,0.85)"); + + // Memory fill on belly (from bottom up) + if (ramUsagePercent > 0) { + const bellyClipId = `tux-belly-${nodeInfo.id.replace(/[^a-zA-Z0-9]/g, "-")}`; + defs + .append("clipPath") + .attr("id", bellyClipId) + .append("path") + .attr("d", TUX_BELLY_PATH) + .attr("transform", `translate(${tx}, ${ty}) scale(${scale})`); + + const bellyTop = ty + 37 * scale; + const bellyHeight = 36 * scale; + const memHeight = (ramUsagePercent / 100) * bellyHeight; + nodeG + .append("rect") + .attr("x", tx + 38 * scale) + .attr("y", bellyTop + bellyHeight - memHeight) + .attr("width", 24 * scale) + .attr("height", memHeight) + .attr("fill", "rgba(255,215,0,0.75)") + .attr("clip-path", `url(#${bellyClipId})`); + } + + // Eyes + const eyeRadius = 2.5 * scale; + nodeG + .append("circle") + .attr("cx", tx + 44 * scale) + .attr("cy", ty + 16 * scale) + .attr("r", eyeRadius) + .attr("fill", "white"); + nodeG + .append("circle") + .attr("cx", tx + 56 * scale) + .attr("cy", ty + 16 * scale) + .attr("r", eyeRadius) + .attr("fill", "white"); + // Pupils + nodeG + .append("circle") + .attr("cx", tx + 44 * scale) + .attr("cy", ty + 16 * scale) + .attr("r", eyeRadius * 0.5) + .attr("fill", "#1a1a1a"); + nodeG + .append("circle") + .attr("cx", tx + 56 * scale) + .attr("cy", ty + 16 * scale) + .attr("r", eyeRadius * 0.5) + .attr("fill", "#1a1a1a"); + + // Beak + nodeG + .append("path") + .attr( + "d", + `M${tx + 46 * scale} ${ty + 22 * scale} L${tx + 50 * scale} ${ty + 27 * scale} L${tx + 54 * scale} ${ty + 22 * scale} Z`, + ) + .attr("fill", "#E8A317"); + + // Feet + nodeG + .append("ellipse") + .attr("cx", tx + 42 * scale) + .attr("cy", ty + 94 * scale) + .attr("rx", 6 * scale) + .attr("ry", 2.5 * scale) + .attr("fill", "#E8A317"); + nodeG + .append("ellipse") + .attr("cx", tx + 58 * scale) + .attr("cy", ty + 94 * scale) + .attr("rx", 6 * scale) + .attr("ry", 2.5 * scale) + .attr("fill", "#E8A317"); + } else if (modelLower === "mac studio") { // Mac Studio - classic cube with memory fill iconBaseWidth = nodeRadius * 1.25; iconBaseHeight = nodeRadius * 0.85; @@ -1182,8 +1486,12 @@ debugLabelY += debugLineHeight; } - const identity = identitiesData[nodeInfo.id]; - if (identity?.osVersion) { + const dbgIdentity = identitiesData[nodeInfo.id]; + if (dbgIdentity?.osVersion) { + const osLabel = + dbgIdentity.osVersion === "Linux" + ? "Linux" + : `macOS ${dbgIdentity.osVersion}${dbgIdentity.osBuildVersion ? ` (${dbgIdentity.osBuildVersion})` : ""}`; nodeG .append("text") .attr("x", nodeInfo.x) @@ -1192,9 +1500,7 @@ .attr("fill", "rgba(179,179,179,0.7)") .attr("font-size", debugFontSize) .attr("font-family", "SF Mono, Monaco, monospace") - .text( - `macOS ${identity.osVersion}${identity.osBuildVersion ? ` (${identity.osBuildVersion})` : ""}`, - ); + .text(osLabel); } } }); diff --git a/src/exo/utils/info_gatherer/system_info.py b/src/exo/utils/info_gatherer/system_info.py index 0cc13f9c..ad8a6754 100644 --- a/src/exo/utils/info_gatherer/system_info.py +++ b/src/exo/utils/info_gatherer/system_info.py @@ -1,6 +1,7 @@ import platform import socket import sys +from pathlib import Path from subprocess import CalledProcessError import psutil @@ -117,12 +118,84 @@ async def get_network_interfaces() -> list[NetworkInterfaceInfo]: return interfaces_info +def _read_dmi_field(name: str) -> str | None: + """Read a single DMI sysfs field, returning ``None`` on failure.""" + try: + path = Path(f"/sys/class/dmi/id/{name}") + if path.exists(): + return path.read_text().strip() + except (OSError, PermissionError): + pass + return None + + +async def _get_linux_model_and_chip() -> tuple[str, str]: + """Get Linux system information using DMI and /proc/cpuinfo. + + Detects NVIDIA DGX Spark (DMI product_name ``"DGX_Spark"``) and other + NVIDIA systems via the ``sys_vendor`` DMI field, falling back to generic + Linux identification. + """ + model = "Linux" + chip = "Unknown Chip" + + product_name = _read_dmi_field("product_name") + sys_vendor = _read_dmi_field("sys_vendor") + + # DGX Spark: DMI product_name may be "DGX_Spark" or "gx10" variant + product_lower = (product_name or "").lower() + if product_name and ("dgx" in product_lower or "gx10" in product_lower): + model = "DGX Spark" + try: + process = await run_process( + ["nvidia-smi", "--query-gpu=name", "--format=csv,noheader"] + ) + gpu_name = process.stdout.decode().strip().split("\n")[0] + chip = gpu_name if gpu_name and gpu_name != "[N/A]" else "NVIDIA GB10" + except (CalledProcessError, FileNotFoundError): + chip = "NVIDIA GB10" + return (model, chip) + + # Other NVIDIA systems (sys_vendor contains "NVIDIA") + if sys_vendor and "NVIDIA" in sys_vendor: + model = product_name.replace("_", " ") if product_name else "NVIDIA System" + try: + process = await run_process( + ["nvidia-smi", "--query-gpu=name", "--format=csv,noheader"] + ) + gpu_name = process.stdout.decode().strip().split("\n")[0] + if gpu_name and gpu_name != "[N/A]": + chip = gpu_name + except (CalledProcessError, FileNotFoundError): + pass + return (model, chip) + + # Generic Linux — use /proc/cpuinfo for chip + cpuinfo_path = Path("/proc/cpuinfo") + if cpuinfo_path.exists(): + try: + for line in cpuinfo_path.read_text().splitlines(): + if line.startswith("model name"): + chip = line.split(":", 1)[1].strip() + break + except OSError: + pass + + return (model, chip) + + async def get_model_and_chip() -> tuple[str, str]: - """Get Mac system information using system_profiler.""" + """Get system model and chip information. + + On macOS, uses ``system_profiler``. On Linux, reads DMI data from + sysfs and CPU info from ``/proc/cpuinfo``. + """ model = "Unknown Model" chip = "Unknown Chip" - # TODO: better non mac support + if sys.platform == "linux": + return await _get_linux_model_and_chip() + if sys.platform != "darwin": return (model, chip) diff --git a/tmp/spark/spark-build-apple-cdc-ncm.sh b/tmp/spark/spark-build-apple-cdc-ncm.sh index f83f4778..0b21c8b8 100644 --- a/tmp/spark/spark-build-apple-cdc-ncm.sh +++ b/tmp/spark/spark-build-apple-cdc-ncm.sh @@ -14,11 +14,14 @@ DSC="${SRCNAME}_${SRCVER}.dsc" KBUILD="/lib/modules/${KVER}/build" msg() { printf '\n==> %s\n' "$*"; } -die() { echo "ERROR: $*" >&2; exit 1; } +die() { + echo "ERROR: $*" >&2 + exit 1 +} need_cmd() { command -v "$1" >/dev/null 2>&1 || die "Missing command: $1"; } [[ $EUID -eq 0 ]] || die "Run with sudo" -[[ -d "$KBUILD" ]] || die "Missing kernel headers/build dir: $KBUILD" +[[ -d $KBUILD ]] || die "Missing kernel headers/build dir: $KBUILD" need_cmd curl need_cmd dpkg-source @@ -52,13 +55,13 @@ cd "$WORKDIR" msg "Downloading exact source package for ${SRCNAME} ${SRCVER}" curl -fL "${BASE_URL}/${ORIG}" -o "${ORIG}" curl -fL "${BASE_URL}/${DIFF}" -o "${DIFF}" -curl -fL "${BASE_URL}/${DSC}" -o "${DSC}" +curl -fL "${BASE_URL}/${DSC}" -o "${DSC}" msg "Extracting source" dpkg-source -x "${DSC}" SRCDIR="$(find . -maxdepth 1 -mindepth 1 -type d -name "${SRCNAME}-*" | head -n1)" -[[ -n "${SRCDIR}" ]] || die "Could not find extracted source directory" +[[ -n ${SRCDIR} ]] || die "Could not find extracted source directory" cd "${SRCDIR}" [[ -f drivers/net/usb/cdc_ncm.c ]] || die "cdc_ncm.c not found in source tree" @@ -127,7 +130,7 @@ msg "Preparing out-of-tree build dir" mkdir -p buildmod cp drivers/net/usb/cdc_ncm.c buildmod/ -cat > buildmod/Makefile <<'EOF' +cat >buildmod/Makefile <<'EOF' obj-m += cdc_ncm.o ccflags-y += -Wno-error @@ -142,7 +145,7 @@ msg "Building patched module" cd buildmod make -C "$KBUILD" M="$PWD" modules PATCHED_KO="$PWD/cdc_ncm.ko" -[[ -f "$PATCHED_KO" ]] || die "Patched cdc_ncm.ko was not built" +[[ -f $PATCHED_KO ]] || die "Patched cdc_ncm.ko was not built" msg "Patched module info" modinfo "$PATCHED_KO" | sed -n '1,20p'