├── .gitignore
├── Makefile
├── autozfs.plist
├── LICENSE
├── README.md
└── autozfs.c
/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 | .DS_Store
3 | autozfs
4 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | CC=clang
2 | CFLAGS=-W -Wall -Wextra -std=c11
3 | LDFLAGS=-framework CoreFoundation -framework DiskArbitration
4 |
5 | autozfs: autozfs.c
6 |
7 | clean:
8 | rm -f autozfs
9 |
10 | .PHONY: clean
11 |
--------------------------------------------------------------------------------
/autozfs.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Label
6 | autozfs
7 | Program
8 | /usr/local/bin/autozfs
9 | KeepAlive
10 |
11 | RunAtLoad
12 |
13 | StandardErrorPath
14 | /private/var/log/autozfs.err
15 | StandardOutPath
16 | /private/var/log/autozfs.log
17 |
18 |
19 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2017, Michael Sproul
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are met:
6 | * Redistributions of source code must retain the above copyright
7 | notice, this list of conditions and the following disclaimer.
8 | * Redistributions in binary form must reproduce the above copyright
9 | notice, this list of conditions and the following disclaimer in the
10 | documentation and/or other materials provided with the distribution.
11 | * Neither the name of the Anarcho Software Collective I Guess nor the
12 | names of its contributors may be used to endorse or promote products
13 | derived from this software without specific prior written permission.
14 |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 | DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY
19 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # autozfs
2 |
3 | Daemon for OS X that listens for external drives being plugged in and automatically imports
4 | ZFS pools.
5 |
6 | If you use or want to use ZFS on an external hard drive, you might find this useful :)
7 |
8 | ## Installation
9 |
10 | You'll need [OpenZFSOnOSX][] or similar installed.
11 |
12 | To build:
13 |
14 | ```bash
15 | $ git clone https://github.com/michaelsproul/autozfs.git
16 | $ cd autozfs
17 | $ make
18 | ```
19 |
20 | To install:
21 |
22 | ```bash
23 | $ sudo cp autozfs /usr/local/bin/
24 | $ sudo cp autozfs.plist /Library/LaunchDaemons/
25 | ```
26 |
27 | Then either reboot, or run:
28 |
29 | ```bash
30 | $ sudo launchctl load /Library/LaunchDaemons/autozfs.plist
31 | ```
32 |
33 | ## How does it work?
34 |
35 | A background daemon started via `launchd` and running as root listens for "Disk Arbitration"
36 | events, and each time it detects a disk connection it runs `zpool import -a`. Simple!
37 |
38 | ## Debugging
39 |
40 | * You should be able to see a process called `autozfs` running, check Activity Monitor, `ps`,
41 | `htop` or whatever.
42 | * The daemon logs inane messages to `/private/var/log/autozfs.{log,err}`.
43 |
44 | ## Caveats
45 |
46 | * OS X only (for now).
47 | * IMPORTS ALL POOLS. This is hacky.
48 | * Assumes `zpool` is installed in `/usr/local/bin`. This may not be true of all systems...
49 | * Only auto-mounts USB devices, but it's an artificial restriction -- delete the USB stuff
50 | in the source and it should do Firewire/Thunderbolt/whatever just fine.
51 |
52 | ## License
53 |
54 | Copyright (c) 2017, Michael Sproul.
55 |
56 | Licensed under the terms of the 3-clause BSD license.
57 |
58 | [OpenZFSOnOSX]: https://openzfsonosx.org/wiki/Downloads
59 |
--------------------------------------------------------------------------------
/autozfs.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 |
7 | #define UNUSED(x) UNUSED_ ## x __attribute__((__unused__))
8 |
9 | // Based on:
10 | // https://developer.apple.com/library/content/documentation/DriversKernelHardware/Conceptual/DiskArbitrationProgGuide/ArbitrationBasics/ArbitrationBasics.html#//apple_ref/doc/uid/TP40009310-CH2-SW2
11 |
12 | void zfsImportAll(DADiskRef UNUSED(disk), void * UNUSED(ctxt)) {
13 | printf("Wubba lubba dub dub! Importing your disks!\n");
14 | fflush(stdout);
15 | int exitCode = system("/usr/local/bin/zpool import -a");
16 | if (exitCode == 0) {
17 | printf("Done! Run `zpool list` to see if anything was imported.\n");
18 | } else {
19 | printf("Oh shit, that failed hard! Exit code for `zpool import -a`: %d\n", exitCode);
20 | }
21 | fflush(stdout);
22 | }
23 |
24 | int main() {
25 | DASessionRef sesh = DASessionCreate(kCFAllocatorDefault);
26 |
27 | CFMutableDictionaryRef matchingDict = CFDictionaryCreateMutable(
28 | kCFAllocatorDefault,
29 | 0,
30 | &kCFTypeDictionaryKeyCallBacks,
31 | &kCFTypeDictionaryValueCallBacks);
32 |
33 | CFDictionaryAddValue(matchingDict,
34 | kDADiskDescriptionDeviceProtocolKey,
35 | CFSTR(kIOPropertyPhysicalInterconnectTypeUSB));
36 |
37 | CFDictionaryAddValue(matchingDict,
38 | kDADiskDescriptionMediaWholeKey,
39 | kCFBooleanTrue);
40 |
41 | DARegisterDiskPeekCallback(sesh, matchingDict, 0, zfsImportAll, NULL);
42 |
43 | DASessionScheduleWithRunLoop(sesh, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
44 | CFRunLoopRun();
45 |
46 | // TODO: maybe run these on Ctrl-C?
47 | DASessionUnscheduleFromRunLoop(sesh, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
48 | CFRelease(sesh);
49 | }
50 |
--------------------------------------------------------------------------------