Skip to content

Latest commit

 

History

History

mac-ping

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 

README.md

MacPing

A tiny macOS menu bar app that does two things:

  1. Internet latency — pings google.com every 5 seconds and shows the round-trip time in the status bar (the headline number).
  2. LAN device scanner — discovers active devices on your Wi-Fi/Ethernet network and lists them in the menu, with as much identifying info as it can gather per device.

No Dock icon, no window — just a menu bar item. No dependencies beyond the Xcode Command Line Tools.

What it shows per device

Open the menu bar item to see the device list. Each device has a submenu with:

  • IP address
  • MAC address (flagged (randomized) when it's a private/locally-administered address — phones rotate these)
  • Vendor — looked up from the MAC's OUI prefix (curated offline table: Apple, Ubiquiti, Raspberry Pi, Samsung, Google, Amazon, Espressif, …)
  • Hostname — reverse DNS (PTR)
  • Bonjour name — the device's advertised mDNS name (e.g. "living-room-speaker", "office-printer")
  • Services — what it advertises over Bonjour (AirPlay, HomeKit, Chromecast, Spotify, Printer, SSH, SMB, …)
  • Ping — round-trip time to that device
  • Role — default gateway (your router) or this Mac

The header also shows the chosen interface + IP and a best-effort Wi-Fi line (signal/channel/rate; SSID requires Location permission).

Build

swiftc main.swift -o MacPing.app/Contents/MacOS/MacPing -framework CoreWLAN

Requires the Xcode Command Line Tools (xcode-select --install). The -framework CoreWLAN flag links the Wi-Fi info API.

Run

open MacPing.app

Launch via open (not by invoking the binary directly). open hands the process to launchd, which spawns it outside any parent shell sandbox — ICMP sockets need this.

First launch: macOS will prompt to allow MacPing to access devices on your local network. Approve it, or the scanner can't see anything (this is required for both the ping-sweep and Bonjour discovery on modern macOS).

Command-line mode (one-shot scan)

The same binary can print a scan to stdout and exit — handy for scripting:

# Human-readable report
./MacPing.app/Contents/MacOS/MacPing --scan-once

# JSON (pipe to jq, etc.)
./MacPing.app/Contents/MacOS/MacPing --json | jq '.devices[] | {ip, vendor, bonjourName}'

How often it scans

What When
Internet ping (the menu bar number) every 5 s
LAN device scan only when you open the menu (click the menu bar item)
LAN device scan (manual) also via Scan now / ⌘R while the menu is open
Bonjour/mDNS discovery continuous in the background (passive listening, no ping traffic)

The LAN scan is click-driven — there's no background timer, so it never ping-sweeps on its own. Each time you open the menu it kicks a fresh scan (ping-sweeps the whole subnet, up to a /22 ≈ 1022 hosts, ~10–15 s); the menu shows the previous results plus "Scanning…" and updates when it finishes. The ping interval is the withTimeInterval: 5 constant in AppDelegate.

Controls

  • Open the menu bar item for the device list; each device has a submenu with Copy this device.
  • Copy report copies the whole scan as text; Scan now (⌘R) forces a rescan.
  • Quit from the menu, or: pkill -x MacPing
  • Rebuild + relaunch:
    swiftc main.swift -o MacPing.app/Contents/MacOS/MacPing -framework CoreWLAN \
      && pkill -x MacPing; open MacPing.app

Layout

mac-ping/
├── main.swift                       # source (ping + scanner + menu + CLI)
├── MacPing.app/
│   └── Contents/
│       ├── Info.plist               # LSUIElement, NSLocalNetworkUsageDescription, NSBonjourServices
│       └── MacOS/MacPing            # compiled binary
└── README.md

How it works

  • NSApp.setActivationPolicy(.accessory) plus LSUIElement in Info.plist makes it a menu-bar-only app.
  • Internet ping: every 5 s, Process runs /sbin/ping -c 1 -W 2000 google.com; the RTT is regex-extracted and shown as the title.
  • LAN scan: picks the LAN interface via getifaddrs (an up, broadcast-capable, non-loopback, non-point-to-point en* with a private IP — this skips VPN utunN tunnels), then:
    1. Ping-sweeps the subnet with bounded concurrency. Discovery relies on ARP, not ping success: sending the packet makes the kernel resolve the host's MAC even if it ignores ICMP echo.
    2. Reads the ARP table (arp -a -n) for IP→MAC, restricted to the chosen interface and filtered to in-subnet unicast hosts (drops multicast/broadcast).
    3. Reverse-DNS each host via getnameinfo.
    4. Looks up the vendor from the MAC OUI.
    5. Merges names/services from a continuous Bonjour/mDNS browse (NetServiceBrowser).
    6. Marks the gateway (per-interface router, so it's correct even behind a VPN) and this Mac.
  • On internet-ping error (DNS, timeout, unreachable), the title shows —.

Auto-start at login (optional)

System Settings → General → Login Items → add MacPing.app.