#macos #disk #monitor #activity #mac #command-line-tool #diskutil

bin+lib mac-disk-monitor

Rust library and command-line tool to monitor disk activity on MacOS

1 unstable release

0.1.0 Jan 10, 2022

#117 in macOS and iOS APIs

GPL-3.0 license

35KB
509 lines

mac-disk-monitor

Rust library and command-line tool to monitor disk activity on MacOS

CI codecov

Usage

Run the code below, then try plugging a USB dongle or opening a DMG file.

use mac_disk_monitor::{stream_events, Action};
use std::sync::mpsc::channel;
use std::time::Duration;

fn main() {
    let (action, receiver) = channel();
    let (thread, receiver) = stream_events(receiver);

    loop {
        // poll for events every 1.5 seconds
        match receiver.recv_timeout(Duration::from_millis(1500)) {
            Ok(event) => match event {
                Some(event) => {
                    println!("{}", event.to_json());
                }
                None => {
                    eprintln!("thread has stopped...");
                }
            },
            Err(e) => {
                eprintln!("Error: {}", e);
                break;
            }
        }
    }
    action.send(Action::Stop).unwrap();
    eprintln!("waiting for thread to stop...");
    thread.join().unwrap().unwrap()
}

Context

This started as a pet project to practice rust.

It executes the command diskutil activity in a thread and parses every stdout line in real-time, extracting structured event data.

Developers should be able to subscribe to events and take action, for example emitting a notification if a specific disk is mounted.

Development

This repo is in active development with best effort to classic Test-Driven Development, commits happen generaly in this order:

  • "TDD [red] - write a failing test that given an input expects an output"
  • "TDD [green] - write the smallest and/or simplest code to make the test pass, probably via hardcoding expected value"
  • "TDD [refactor] - write real code to make the test pass

Other than that, all other commits kind of follow conventional commits.

Test Data

Click here to see the test data used as input for the "unit" tests
***Begin monitoring DiskArbitration activity
***DiskAppeared ('disk4', DAVolumePath = 'file:///Volumes/my%20backups/', DAVolumeKind = 'hfs', DAVolumeName = 'Time Machine Backups') Time=20220108-20:22:05.1438
***DiskAppeared ('disk3s2', DAVolumePath = '<null>', DAVolumeKind = '<null>', DAVolumeName = '<null>') Time=20220108-20:22:05.1453
***DiskAppeared ('disk3s1', DAVolumePath = '<null>', DAVolumeKind = 'msdos', DAVolumeName = 'EFI') Time=20220108-20:22:05.1454
***DiskAppeared ('disk3s3', DAVolumePath = '<null>', DAVolumeKind = 'hfs', DAVolumeName = 'Boot OS X') Time=20220108-20:22:05.1455
***DiskAppeared ('disk3', DAVolumePath = '<null>', DAVolumeKind = '<null>', DAVolumeName = '<null>') Time=20220108-20:22:05.1456
***DiskAppeared ((no BSD name), DAVolumePath = 'file:///System/Volumes/Data/home/', DAVolumeKind = 'autofs', DAVolumeName = '<null>') Time=20220108-20:22:05.1457
***DiskAppeared ('disk2s1', DAVolumePath = 'file:///Volumes/garuda-ext/', DAVolumeKind = 'hfs', DAVolumeName = 'garuda-ext') Time=20220108-20:22:05.1458
***DiskAppeared ('disk2', DAVolumePath = '<null>', DAVolumeKind = '<null>', DAVolumeName = '<null>') Time=20220108-20:22:05.1459
***DiskAppeared ('disk0', DAVolumePath = '<null>', DAVolumeKind = '<null>', DAVolumeName = '<null>') Time=20220108-20:22:05.1460
***DiskAppeared ('disk0s1', DAVolumePath = '<null>', DAVolumeKind = 'msdos', DAVolumeName = 'EFI') Time=20220108-20:22:05.1461
***DiskAppeared ('disk0s2', DAVolumePath = '<null>', DAVolumeKind = '<null>', DAVolumeName = '<null>') Time=20220108-20:22:05.1462
***DiskAppeared ('disk1', DAVolumePath = '<null>', DAVolumeKind = '<null>', DAVolumeName = '<null>') Time=20220108-20:22:05.1463
***DiskAppeared ('disk1s1', DAVolumePath = 'file:///System/Volumes/Data/', DAVolumeKind = 'apfs', DAVolumeName = 'maindisk - Data') Time=20220108-20:22:05.1464
***DiskAppeared ('disk1s2', DAVolumePath = '<null>', DAVolumeKind = 'apfs', DAVolumeName = 'Preboot') Time=20220108-20:22:05.1465
***DiskAppeared ('disk1s3', DAVolumePath = 'file:///Volumes/Recovery/', DAVolumeKind = 'apfs', DAVolumeName = 'Recovery') Time=20220108-20:22:05.1466
***DiskAppeared ('disk1s4', DAVolumePath = 'file:///private/var/vm/', DAVolumeKind = 'apfs', DAVolumeName = 'VM') Time=20220108-20:22:05.1467
***DiskAppeared ('disk1s5', DAVolumePath = 'file:///', DAVolumeKind = 'apfs', DAVolumeName = 'maindisk') Time=20220108-20:22:05.1469
***DAIdle (no DADiskRef) Time=20220108-20:22:05.1470
***DiskUnmountApproval ('disk4', DAVolumePath = 'file:///Volumes/my%20backups/', DAVolumeKind = 'hfs', DAVolumeName = 'Time Machine Backups') Comment=Approving Time=20220108-20:22:09.9084
***DiskUnmountApproval ('disk4', DAVolumePath = 'file:///Volumes/my%20backups/', DAVolumeKind = 'hfs', DAVolumeName = 'Time Machine Backups') Comment=Approving Time=20220108-20:22:21.1909
***DiskDescriptionChanged ('disk4', DAVolumePath = '<null>') Time=20220108-20:22:21.5683
***DAIdle (no DADiskRef) Time=20220108-20:22:21.5684
***DiskDisappeared ('disk3', DAVolumePath = '<null>', DAVolumeKind = '<null>', DAVolumeName = '<null>') Time=20220108-20:22:29.6767
***DiskDisappeared ('disk4', DAVolumePath = '<null>', DAVolumeKind = 'hfs', DAVolumeName = 'Time Machine Backups') Time=20220108-20:22:29.6768
***DiskDisappeared ('disk3s3', DAVolumePath = '<null>', DAVolumeKind = 'hfs', DAVolumeName = 'Boot OS X') Time=20220108-20:22:29.6770
***DiskDisappeared ('disk3s2', DAVolumePath = '<null>', DAVolumeKind = '<null>', DAVolumeName = '<null>') Time=20220108-20:22:29.6772
***DiskDisappeared ('disk3s1', DAVolumePath = '<null>', DAVolumeKind = 'msdos', DAVolumeName = 'EFI') Time=20220108-20:22:29.6773
***DAIdle (no DADiskRef) Time=20220108-20:22:29.6774
***DiskPeek ('disk3s1') Time=20220108-20:22:35.8607
***DiskAppeared ('disk3s1', DAVolumePath = '<null>', DAVolumeKind = 'msdos', DAVolumeName = 'EFI') Time=20220108-20:22:35.8673
***DiskMountApproval ('disk3s1', DAVolumePath = '<null>', DAVolumeKind = 'msdos', DAVolumeName = 'EFI') Comment=Approving Time=20220108-20:22:35.8686
***DiskPeek ('disk3s3') Time=20220108-20:22:36.0009
***DiskPeek ('disk3s2') Time=20220108-20:22:36.0011
***DiskPeek ('disk3') Time=20220108-20:22:36.0014
***DiskAppeared ('disk3s3', DAVolumePath = '<null>', DAVolumeKind = 'hfs', DAVolumeName = 'Boot OS X') Time=20220108-20:22:36.0040
***DiskMountApproval ('disk3s3', DAVolumePath = '<null>', DAVolumeKind = 'hfs', DAVolumeName = 'Boot OS X') Comment=Approving Time=20220108-20:22:36.0065
***DiskAppeared ('disk3s2', DAVolumePath = '<null>', DAVolumeKind = '<null>', DAVolumeName = '<null>') Time=20220108-20:22:36.0116
***DiskAppeared ('disk3', DAVolumePath = '<null>', DAVolumeKind = '<null>', DAVolumeName = '<null>') Time=20220108-20:22:36.0118
***DAIdle (no DADiskRef) Time=20220108-20:22:36.0119
***DiskPeek ('disk4') Time=20220108-20:22:37.5920
***DiskAppeared ('disk4', DAVolumePath = '<null>', DAVolumeKind = 'hfs', DAVolumeName = 'Time Machine Backups') Time=20220108-20:22:37.5980
***DiskMountApproval ('disk4', DAVolumePath = '<null>', DAVolumeKind = 'hfs', DAVolumeName = 'Time Machine Backups') Comment=Approving Time=20220108-20:22:37.5985
***DiskDescriptionChanged ('disk4', DAVolumePath = 'file:///Volumes/my%20backups/') Time=20220108-20:22:41.5508
***DAIdle (no DADiskRef) Time=20220108-20:22:41.5509
***DiskUnmountApproval ('disk4', DAVolumePath = 'file:///Volumes/my%20backups/', DAVolumeKind = 'hfs', DAVolumeName = 'Time Machine Backups') Comment=Approving Time=20220108-20:22:58.3459
***DiskUnmountApproval ('disk4', DAVolumePath = 'file:///Volumes/my%20backups/', DAVolumeKind = 'hfs', DAVolumeName = 'Time Machine Backups') Comment=Approving Time=20220108-20:23:09.6402
***DiskDescriptionChanged ('disk4', DAVolumePath = '<null>') Time=20220108-20:23:10.0281
***DAIdle (no DADiskRef) Time=20220108-20:23:10.0282
***DiskDisappeared ('disk3', DAVolumePath = '<null>', DAVolumeKind = '<null>', DAVolumeName = '<null>') Time=20220108-20:26:42.3640
***DiskDisappeared ('disk4', DAVolumePath = '<null>', DAVolumeKind = 'hfs', DAVolumeName = 'Time Machine Backups') Time=20220108-20:26:42.3642
***DiskDisappeared ('disk3s3', DAVolumePath = '<null>', DAVolumeKind = 'hfs', DAVolumeName = 'Boot OS X') Time=20220108-20:26:42.3643
***DiskDisappeared ('disk3s2', DAVolumePath = '<null>', DAVolumeKind = '<null>', DAVolumeName = '<null>') Time=20220108-20:26:42.3645
***DiskDisappeared ('disk3s1', DAVolumePath = '<null>', DAVolumeKind = 'msdos', DAVolumeName = 'EFI') Time=20220108-20:26:42.3647
***DAIdle (no DADiskRef) Time=20220108-20:26:42.3647
***DiskPeek ('disk3s1') Time=20220108-20:26:48.0983
***DiskAppeared ('disk3s1', DAVolumePath = '<null>', DAVolumeKind = 'msdos', DAVolumeName = 'EFI') Time=20220108-20:26:48.1052
***DiskMountApproval ('disk3s1', DAVolumePath = '<null>', DAVolumeKind = 'msdos', DAVolumeName = 'EFI') Comment=Approving Time=20220108-20:26:48.1069
***DiskPeek ('disk3s3') Time=20220108-20:26:48.2289
***DiskPeek ('disk3s2') Time=20220108-20:26:48.2291
***DiskPeek ('disk3') Time=20220108-20:26:48.2292
***DiskAppeared ('disk3s3', DAVolumePath = '<null>', DAVolumeKind = 'hfs', DAVolumeName = 'Boot OS X') Time=20220108-20:26:48.2317
***DiskMountApproval ('disk3s3', DAVolumePath = '<null>', DAVolumeKind = 'hfs', DAVolumeName = 'Boot OS X') Comment=Approving Time=20220108-20:26:48.2345
***DiskAppeared ('disk3s2', DAVolumePath = '<null>', DAVolumeKind = '<null>', DAVolumeName = '<null>') Time=20220108-20:26:48.2418
***DiskAppeared ('disk3', DAVolumePath = '<null>', DAVolumeKind = '<null>', DAVolumeName = '<null>') Time=20220108-20:26:48.2419
***DAIdle (no DADiskRef) Time=20220108-20:26:48.2420
***DiskPeek ('disk4') Time=20220108-20:26:49.4535
***DiskAppeared ('disk4', DAVolumePath = '<null>', DAVolumeKind = 'hfs', DAVolumeName = 'Time Machine Backups') Time=20220108-20:26:49.4590
***DiskMountApproval ('disk4', DAVolumePath = '<null>', DAVolumeKind = 'hfs', DAVolumeName = 'Time Machine Backups') Comment=Approving Time=20220108-20:26:49.4594
***DiskDescriptionChanged ('disk4', DAVolumePath = 'file:///Volumes/my%20backups/') Time=20220108-20:26:52.7814
***DAIdle (no DADiskRef) Time=20220108-20:26:52.7814

Dependencies

~7–16MB
~180K SLoC