diff options
Diffstat (limited to '')
-rw-r--r-- | os_darwin.cpp | 782 | ||||
-rw-r--r-- | os_darwin.h | 80 | ||||
-rw-r--r-- | os_darwin/com.smartmontools.smartd.plist.in | 20 | ||||
-rw-r--r-- | os_darwin/pkg/Distribution.in | 21 | ||||
-rw-r--r-- | os_darwin/pkg/PackageInfo.in | 6 | ||||
-rw-r--r-- | os_darwin/pkg/installer/README.html | 26 | ||||
-rwxr-xr-x | os_darwin/pkg/root/usr/local/sbin/smart-pkg-uninstall | 40 |
7 files changed, 975 insertions, 0 deletions
diff --git a/os_darwin.cpp b/os_darwin.cpp new file mode 100644 index 0000000..74960b3 --- /dev/null +++ b/os_darwin.cpp @@ -0,0 +1,782 @@ +/* + * os_darwin.cpp + * + * Home page of code is: https://www.smartmontools.org + * + * Copyright (C) 2004-8 Geoffrey Keating <geoffk@geoffk.org> + * Copyright (C) 2014 Alex Samorukov <samm@os2.kiev.ua> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include <stdbool.h> +#include <errno.h> +#include <unistd.h> +#include <mach/mach.h> +#include <mach/mach_error.h> +#include <mach/mach_init.h> +#include <sys/utsname.h> +#include <IOKit/IOCFPlugIn.h> +#include <IOKit/IOKitLib.h> +#include <IOKit/IOReturn.h> +#include <IOKit/IOBSD.h> +#include <IOKit/storage/IOBlockStorageDevice.h> +#include <IOKit/storage/IOStorageDeviceCharacteristics.h> +#include <IOKit/storage/IOMedia.h> +#include <IOKit/storage/ata/IOATAStorageDefines.h> +#include <IOKit/storage/ata/ATASMARTLib.h> +#include <CoreFoundation/CoreFoundation.h> + +#include "config.h" + +#include "atacmds.h" +#include "scsicmds.h" +#include "nvmecmds.h" +#include "utility.h" +#include "os_darwin.h" +#include "dev_interface.h" + +#define ARGUSED(x) ((void)(x)) +// Needed by '-V' option (CVS versioning) of smartd/smartctl +const char *os_darwin_cpp_cvsid="$Id: os_darwin.cpp 5209 2021-02-14 18:02:51Z samm2 $" \ +ATACMDS_H_CVSID CONFIG_H_CVSID OS_DARWIN_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID; + +// examples for smartctl +static const char smartctl_examples[] = + "=================================================== SMARTCTL EXAMPLES =====\n\n" + " smartctl -a disk0 (Prints all SMART information)\n\n" + " smartctl -t long /dev/disk0 (Executes extended disk self-test)\n\n" + " smartctl --smart=on --saveauto=on /dev/rdisk0 (Enables SMART on first disk)\n\n" + " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/disk0\n" + " (Prints Self-Test & Attribute errors)\n\n" + " smartctl -a IOService:/MacRISC2PE/pci@f4000000/AppleMacRiscPCI/ata-6@D/AppleKauaiATA/ATADeviceNub@0/IOATABlockStorageDriver/IOATABlockStorageDevice\n" + " (You can use IOService: ...)\n\n" + " smartctl -c IODeviceTree:/pci@f4000000/ata-6@D/@0:0\n" + " (... Or IODeviceTree:)\n" + ; + + +// Information that we keep about each device. + +static struct { + io_object_t ioob; + IOCFPlugInInterface **plugin; + IOATASMARTInterface **smartIf; // ATA devices + IONVMeSMARTInterface **smartIfNVMe; +} devices[20]; + +const char * dev_darwin_cpp_cvsid = "$Id: os_darwin.cpp 5209 2021-02-14 18:02:51Z samm2 $" + DEV_INTERFACE_H_CVSID; + +///////////////////////////////////////////////////////////////////////////// + +namespace os { // No need to publish anything, name provided for Doxygen + +///////////////////////////////////////////////////////////////////////////// +/// Implement shared open/close routines with old functions. + +class darwin_smart_device +: virtual public /*implements*/ smart_device +{ +public: + explicit darwin_smart_device(const char * mode) + : smart_device(never_called), + m_fd(-1), m_mode(mode) { } + + virtual ~darwin_smart_device(); + + virtual bool is_open() const override; + + virtual bool open() override; + + virtual bool close() override; + +protected: + /// Return filedesc for derived classes. + int get_fd() const + { return m_fd; } + +private: + int m_fd; ///< filedesc, -1 if not open. + const char * m_mode; ///< Mode string for deviceopen(). +}; + + +darwin_smart_device::~darwin_smart_device() +{ + if (m_fd >= 0) + darwin_smart_device::close(); +} + +bool darwin_smart_device::is_open() const +{ + return (m_fd >= 0); +} + +// Determine whether 'dev' is a SMART-capable device. +static bool is_smart_capable (io_object_t dev, const char * type) { + CFTypeRef smartCapableKey = NULL; + CFDictionaryRef diskChars; + + // If the device has kIOPropertySMARTCapableKey, then it's capable, + // no matter what it looks like. + if (!strcmp("ATA", type)) { + smartCapableKey = IORegistryEntryCreateCFProperty + (dev, CFSTR (kIOPropertySMARTCapableKey), + kCFAllocatorDefault, 0); + } + + else if (!strcmp("NVME", type)) { + smartCapableKey = IORegistryEntryCreateCFProperty + (dev, CFSTR (kIOPropertyNVMeSMARTCapableKey), + kCFAllocatorDefault, 0); + } + + if (smartCapableKey) + { + CFRelease (smartCapableKey); + return true; + } + + // If it's an kIOATABlockStorageDeviceClass then we're successful + // only if its ATA features indicate it supports SMART. + // This will be broken for NVMe, however it is not needed + if (IOObjectConformsTo (dev, kIOATABlockStorageDeviceClass) + && (diskChars = (CFDictionaryRef)IORegistryEntryCreateCFProperty + (dev, CFSTR (kIOPropertyDeviceCharacteristicsKey), + kCFAllocatorDefault, kNilOptions)) != NULL) + { + CFNumberRef diskFeatures = NULL; + UInt32 ataFeatures = 0; + + if (CFDictionaryGetValueIfPresent (diskChars, CFSTR ("ATA Features"), + (const void **)&diskFeatures)) + CFNumberGetValue (diskFeatures, kCFNumberLongType, + &ataFeatures); + CFRelease (diskChars); + if (diskFeatures) + CFRelease (diskFeatures); + + return (ataFeatures & kIOATAFeatureSMART) != 0; + } + return false; +} + +bool darwin_smart_device::open() +{ + // Acceptable device names are: + // /dev/disk* + // /dev/rdisk* + // disk* + // IOService:* + // IODeviceTree:* + size_t devnum; + const char *devname; + io_object_t disk; + const char *pathname = get_dev_name(); + char *type = const_cast<char*>(m_mode); + + if (!(strcmp("ATA", type) || strcmp("NVME", type))) + { + set_err (EINVAL); + return false; + } + + // Find a free device number. + for (devnum = 0; devnum < sizeof (devices) / sizeof (devices[0]); devnum++) + if (! devices[devnum].ioob) + break; + if (devnum == sizeof (devices) / sizeof (devices[0])) + { + set_err (EMFILE); + return false; + } + + devname = NULL; + if (strncmp (pathname, "/dev/rdisk", 10) == 0) + devname = pathname + 6; + else if (strncmp (pathname, "/dev/disk", 9) == 0) + devname = pathname + 5; + else if (strncmp (pathname, "disk", 4) == 0) + // allow user to just say 'disk0' + devname = pathname; + + // Find the device. This part should be the same for the NVMe and ATA + if (devname) + { + CFMutableDictionaryRef matcher; + matcher = IOBSDNameMatching (kIOMasterPortDefault, 0, devname); + disk = IOServiceGetMatchingService (kIOMasterPortDefault, matcher); + } + else + { + disk = IORegistryEntryFromPath (kIOMasterPortDefault, pathname); + } + if (! disk) + { + set_err(ENOENT); + return false; + } + // Find a SMART-capable driver which is a parent of this device. + while (! is_smart_capable (disk, type)) + { + IOReturn err; + io_object_t prevdisk = disk; + + // Find this device's parent and try again. + err = IORegistryEntryGetParentEntry (disk, kIOServicePlane, &disk); + if (err != kIOReturnSuccess || ! disk) + { + set_err(ENODEV); + IOObjectRelease (prevdisk); + return false; + } + } + + devices[devnum].ioob = disk; + + { + SInt32 dummy; + + devices[devnum].plugin = NULL; + devices[devnum].smartIf = NULL; + devices[devnum].smartIfNVMe = NULL; + + CFUUIDRef pluginType = NULL; + CFUUIDRef smartInterfaceId = NULL; + void ** SMARTptr = NULL; + + if (!strcmp("ATA", type)) { + pluginType = kIOATASMARTUserClientTypeID; + smartInterfaceId = kIOATASMARTInterfaceID; + SMARTptr = (void **)&devices[devnum].smartIf; + } + else if (!strcmp("NVME", type)) { + pluginType = kIONVMeSMARTUserClientTypeID; + smartInterfaceId = kIONVMeSMARTInterfaceID; + SMARTptr = (void **)&devices[devnum].smartIfNVMe; + } + + // Create an interface to the ATA SMART library. + if (IOCreatePlugInInterfaceForService (disk, + pluginType, + kIOCFPlugInInterfaceID, + &devices[devnum].plugin, + &dummy) == kIOReturnSuccess) + (*devices[devnum].plugin)->QueryInterface + (devices[devnum].plugin, + CFUUIDGetUUIDBytes ( smartInterfaceId), + SMARTptr); + else + return set_err(ENOSYS, "IOCreatePlugInInterfaceForService failed"); + } + + + m_fd = devnum; + if (m_fd < 0) { + set_err((errno==ENOENT || errno==ENOTDIR) ? ENODEV : errno); + return false; + } + return true; +} + +bool darwin_smart_device::close() +{ + int fd = m_fd; m_fd = -1; + if (devices[fd].smartIf) + (*devices[fd].smartIf)->Release (devices[fd].smartIf); + if (devices[fd].smartIfNVMe) + (*devices[fd].smartIfNVMe)->Release (devices[fd].smartIfNVMe); + if (devices[fd].plugin) + IODestroyPlugInInterface (devices[fd].plugin); + IOObjectRelease (devices[fd].ioob); + devices[fd].ioob = MACH_PORT_NULL; + return true; +} + +// makes a list of ATA or SCSI devices for the DEVICESCAN directive of +// smartd. Returns number N of devices, or -1 if out of +// memory. Allocates N+1 arrays: one of N pointers (devlist); the +// other N arrays each contain null-terminated character strings. In +// the case N==0, no arrays are allocated because the array of 0 +// pointers has zero length, equivalent to calling malloc(0). +static int make_device_names (char*** devlist, const char* name) { + IOReturn err; + io_iterator_t i; + io_object_t device = MACH_PORT_NULL; + int result; + int index; + + if (!(strcmp("ATA", name) || strcmp("NVME", name))) { + return 0; + } + + err = IOServiceGetMatchingServices + (kIOMasterPortDefault, IOServiceMatching (kIOBlockStorageDeviceClass), &i); + if (err != kIOReturnSuccess) + return -1; + + // Count the devices. + result = 0; + while ((device = IOIteratorNext (i)) != MACH_PORT_NULL) { + if (is_smart_capable (device, name)) + result++; + IOObjectRelease (device); + } + + // Create an array of service names. + IOIteratorReset (i); + if (! result) + goto error; + *devlist = (char**)calloc (result, sizeof (char *)); + index = 0; + while ((device = IOIteratorNext (i)) != MACH_PORT_NULL) { + if (is_smart_capable (device, name)) + { + io_string_t devName; + IORegistryEntryGetPath(device, kIOServicePlane, devName); + (*devlist)[index] = strdup (devName); + if (! (*devlist)[index]) + goto error; + index++; + } + IOObjectRelease (device); + } + + IOObjectRelease (i); + return result; + + error: + if (device != MACH_PORT_NULL) + IOObjectRelease (device); + IOObjectRelease (i); + if (*devlist) + { + for (index = 0; index < result; index++) + if ((*devlist)[index]) + free ((*devlist)[index]); + free (*devlist); + } + if(!result) // no devs found + return 0; + + return -1; +} + +///////////////////////////////////////////////////////////////////////////// +/// Implement standard ATA support + +class darwin_ata_device +: public /*implements*/ ata_device, + public /*extends*/ darwin_smart_device +{ +public: + darwin_ata_device(smart_interface * intf, const char * dev_name, const char * req_type); + virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) override; + +protected: + // virtual int ata_command_interface(smart_command_set command, int select, char * data); +}; + +darwin_ata_device::darwin_ata_device(smart_interface * intf, const char * dev_name, const char * req_type) +: smart_device(intf, dev_name, "ata", req_type), + darwin_smart_device("ATA") +{ +} + +bool darwin_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) +{ + if (!ata_cmd_is_ok(in, + true, // data_out_support + true, // multi_sector_support + false) // not supported by API + ) + return false; + + int select = 0; + char * data = (char *)in.buffer; + int fd = get_fd(); + IOATASMARTInterface **ifp = devices[fd].smartIf; + IOATASMARTInterface *smartIf; + io_object_t disk = devices[fd].ioob; + IOReturn err; + int timeoutCount = 5; + int rc = 0; + + if (! ifp) + return false; + smartIf = *ifp; + clear_err(); errno = 0; + do { + switch (in.in_regs.command) { + case ATA_IDENTIFY_DEVICE: + { + UInt32 dummy; + err = smartIf->GetATAIdentifyData (ifp, data, 512, &dummy); + if (err != kIOReturnSuccess && err != kIOReturnTimeout + && err != kIOReturnNotResponding) + printf ("identify failed: %#x\n", (unsigned) rc); + if (err == kIOReturnSuccess && isbigendian()) + { + int i; + /* The system has already byte-swapped, undo it. */ + for (i = 0; i < 256; i+=2) + swap2 (data + i); + } + } + break; + case ATA_IDENTIFY_PACKET_DEVICE: + case ATA_CHECK_POWER_MODE: + errno = ENOTSUP; + err = -1; + break; + case ATA_SET_FEATURES: + switch(in.in_regs.features) { + case ATA_ENABLE_APM: + if (in.in_regs.sector_count) { + int l = (int) in.in_regs.sector_count; + CFNumberRef cfLevel = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &l); + kern_return_t r = IORegistryEntrySetCFProperty(disk, CFSTR("APM Level"), cfLevel); + CFRelease(cfLevel); + if (r) { + switch(r) { + case kIOReturnNotPrivileged: + return set_err(ENOSYS, "Use superuser to manage APM"); + break; + case kIOReturnUnsupported: + return set_err(ENOSYS, "APM not supported"); + break; + default: + return set_err(ENOSYS, "APM error: %u", r); + } + } + break; + } + default: + return set_err(ENOSYS, "Unsupported ATA feature"); + } + break; + case ATA_SMART_CMD: + switch (in.in_regs.features) { + case ATA_SMART_READ_VALUES: + err = smartIf->SMARTReadData (ifp, (ATASMARTData *)data); + break; + case ATA_SMART_READ_THRESHOLDS: + err = smartIf->SMARTReadDataThresholds (ifp, + (ATASMARTDataThresholds *)data); + break; + case ATA_SMART_READ_LOG_SECTOR: + err = smartIf->SMARTReadLogAtAddress (ifp, in.in_regs.lba_low, data, 512 * in.in_regs.sector_count); + break; + case ATA_SMART_WRITE_LOG_SECTOR: + err = smartIf->SMARTWriteLogAtAddress (ifp, in.in_regs.lba_low, data, 512 * in.in_regs.sector_count); + break; + case ATA_SMART_ENABLE: + case ATA_SMART_DISABLE: + err = smartIf->SMARTEnableDisableOperations (ifp, in.in_regs.features == ATA_SMART_ENABLE); + break; + case ATA_SMART_STATUS: + if (in.out_needed.lba_high) // statuscheck + { + Boolean is_failing; + err = smartIf->SMARTReturnStatus (ifp, &is_failing); + if (err == kIOReturnSuccess && is_failing) { + err = -1; // thresholds exceeded condition + out.out_regs.lba_high = 0x2c; out.out_regs.lba_mid = 0xf4; + } + else { + out.out_regs.lba_high = 0xc2; out.out_regs.lba_mid = 0x4f; + } + break; + } + else err = 0; + break; + case ATA_SMART_AUTOSAVE: + err = smartIf->SMARTEnableDisableAutosave (ifp, + (in.in_regs.sector_count == 241 ? true : false)); + break; + case ATA_SMART_IMMEDIATE_OFFLINE: + select = in.in_regs.lba_low; + if (select != SHORT_SELF_TEST && select != EXTEND_SELF_TEST) + { + errno = EINVAL; + return set_err(ENOSYS, "Unsupported SMART self-test mode"); + } + err = smartIf->SMARTExecuteOffLineImmediate (ifp, + select == EXTEND_SELF_TEST); + break; + case ATA_SMART_AUTO_OFFLINE: + return set_err(ENOSYS, "SMART command not supported"); + default: + return set_err(ENOSYS, "Unknown SMART command"); + } + break; + default: + return set_err(ENOSYS, "Non-SMART commands not implemented"); + } + } while ((err == kIOReturnTimeout || err == kIOReturnNotResponding) + && timeoutCount-- > 0); + if (err == kIOReturnExclusiveAccess) + errno = EBUSY; + rc = err == kIOReturnSuccess ? 0 : -1; + if (rc < 0) { + if (!get_errno()) + set_err(errno); + return false; + } + return true; +} + +///////////////////////////////////////////////////////////////////////////// +/// Implement platform interface + +class darwin_smart_interface +: public /*implements*/ smart_interface +{ +public: + virtual std::string get_os_version_str() override; + + virtual std::string get_app_examples(const char * appname) override; + + virtual bool scan_smart_devices(smart_device_list & devlist, const char * type, + const char * pattern = 0) override; + +protected: + virtual ata_device * get_ata_device(const char * name, const char * type) override; + + virtual scsi_device * get_scsi_device(const char * name, const char * type) override; + + virtual nvme_device * get_nvme_device(const char * name, const char * type, + unsigned nsid) override; + + virtual smart_device * autodetect_smart_device(const char * name) override; + +}; + +///////////////////////////////////////////////////////////////////////////// +/// NVMe support + +class darwin_nvme_device +: public /*implements*/ nvme_device, + public /*extends*/ darwin_smart_device +{ +public: + darwin_nvme_device(smart_interface * intf, const char * dev_name, + const char * req_type, unsigned nsid); + + virtual bool nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out) override; +}; + +darwin_nvme_device::darwin_nvme_device(smart_interface * intf, const char * dev_name, + const char * req_type, unsigned nsid) +: smart_device(intf, dev_name, "nvme", req_type), + nvme_device(nsid), + darwin_smart_device("NVME") +{ +} + +bool darwin_nvme_device::nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out) +{ + ARGUSED(out); + int fd = get_fd(); + IONVMeSMARTInterface **ifp = devices[fd].smartIfNVMe; + IONVMeSMARTInterface *smartIfNVMe ; + IOReturn err = 0; + unsigned int page = in.cdw10 & 0xff; + + if (! ifp) + return false; + smartIfNVMe = *ifp; + // currently only GetIdentifyData and GetLogPage are supported + switch (in.opcode) { + case smartmontools::nvme_admin_identify: + err = smartIfNVMe->GetIdentifyData(ifp, (struct nvme_id_ctrl *) in.buffer, in.nsid); + if (err) + return set_err(ENOSYS, "GetIdentifyData failed: system=0x%x, sub=0x%x, code=%d", + err_get_system(err), err_get_sub(err), err_get_code(err)); + break; + case smartmontools::nvme_admin_get_log_page: + err = smartIfNVMe->GetLogPage(ifp, in.buffer, page, in.size / 4 - 1); + if (err) + return set_err(ENOSYS, "GetLogPage failed: system=0x%x, sub=0x%x, code=%d", + err_get_system(err), err_get_sub(err), err_get_code(err)); + break; + default: + return set_err(ENOSYS, "NVMe admin command 0x%02x is not supported", in.opcode); + } + return true; +} +////////////////////////////////////////////////////////////////////// + +std::string darwin_smart_interface::get_os_version_str() +{ + // now we are just getting darwin runtime version, to get OSX version more things needs to be done, see + // http://stackoverflow.com/questions/11072804/how-do-i-determine-the-os-version-at-runtime-in-os-x-or-ios-without-using-gesta + struct utsname osname; + uname(&osname); + return strprintf("%s %s %s", osname.sysname, osname.release, osname.machine); +} + +std::string darwin_smart_interface::get_app_examples(const char * appname) +{ + if (!strcmp(appname, "smartctl")) + return smartctl_examples; + return ""; // ... so don't print again. +} + +ata_device * darwin_smart_interface::get_ata_device(const char * name, const char * type) +{ + return new darwin_ata_device(this, name, type); +} + +scsi_device * darwin_smart_interface::get_scsi_device(const char *, const char *) +{ + return 0; // scsi devices are not supported [yet] +} + +nvme_device * darwin_smart_interface::get_nvme_device(const char * name, const char * type, + unsigned nsid) +{ + return new darwin_nvme_device(this, name, type, nsid); +} + +smart_device * darwin_smart_interface::autodetect_smart_device(const char * name) +{ // TODO - refactor as a function + // Acceptable device names are: + // /dev/disk* + // /dev/rdisk* + // disk* + // IOService:* + // IODeviceTree:* + const char *devname = NULL; + io_object_t disk; + + if (strncmp (name, "/dev/rdisk", 10) == 0) + devname = name + 6; + else if (strncmp (name, "/dev/disk", 9) == 0) + devname = name + 5; + else if (strncmp (name, "disk", 4) == 0) + // allow user to just say 'disk0' + devname = name; + // Find the device. This part should be the same for the NVMe and ATA + if (devname) { + CFMutableDictionaryRef matcher; + matcher = IOBSDNameMatching (kIOMasterPortDefault, 0, devname); + disk = IOServiceGetMatchingService (kIOMasterPortDefault, matcher); + } + else { + disk = IORegistryEntryFromPath (kIOMasterPortDefault, name); + } + if (! disk) { + return 0; + } + io_registry_entry_t tmpdisk=disk; + + + while (! is_smart_capable (tmpdisk, "ATA")) + { + IOReturn err; + io_object_t prevdisk = tmpdisk; + + // Find this device's parent and try again. + err = IORegistryEntryGetParentEntry (tmpdisk, kIOServicePlane, &tmpdisk); + if (err != kIOReturnSuccess || ! tmpdisk) + { + IOObjectRelease (prevdisk); + break; + } + } + if (tmpdisk) + return new darwin_ata_device(this, name, ""); + tmpdisk=disk; + while (! is_smart_capable (tmpdisk, "NVME")) + { + IOReturn err; + io_object_t prevdisk = tmpdisk; + + // Find this device's parent and try again. + err = IORegistryEntryGetParentEntry (tmpdisk, kIOServicePlane, &tmpdisk); + if (err != kIOReturnSuccess || ! tmpdisk) + { + IOObjectRelease (prevdisk); + break; + } + } + if (tmpdisk) + return new darwin_nvme_device(this, name, "", 0); + + // try ATA as a last option, for compatibility + return new darwin_ata_device(this, name, ""); +} + +static void free_devnames(char * * devnames, int numdevs) +{ + for (int i = 0; i < numdevs; i++) + free(devnames[i]); + free(devnames); +} + +bool darwin_smart_interface::scan_smart_devices(smart_device_list & devlist, + const char * type, const char * pattern /*= 0*/) +{ + if (pattern) { + set_err(EINVAL, "DEVICESCAN with pattern not implemented yet"); + return false; + } + + // Make namelists + char * * atanames = 0; int numata = 0; + if (!type || !strcmp(type, "ata")) { + numata = make_device_names(&atanames, "ATA"); + if (numata < 0) { + set_err(ENOMEM); + return false; + } + } + char * * nvmenames = 0; int numnvme = 0; + if ( +#ifdef WITH_NVME_DEVICESCAN // TODO: Remove when NVMe support is no longer EXPERIMENTAL + !type || +#else + type && +#endif + !strcmp(type, "nvme")) { + numnvme = make_device_names(&nvmenames, "NVME"); + if (numnvme < 0) { + set_err(ENOMEM); + return false; + } + } + + // Add to devlist + int i; + if (!type) + type=""; + for (i = 0; i < numata; i++) { + ata_device * atadev = get_ata_device(atanames[i], type); + if (atadev) + devlist.push_back(atadev); + } + free_devnames(atanames, numata); + + for (i = 0; i < numnvme; i++) { + nvme_device * nvmedev = get_nvme_device(nvmenames[i], type, 0); // default nsid + if (nvmedev) + devlist.push_back(nvmedev); + } + free_devnames(nvmenames, numnvme); + + return true; +} + +} // namespace + + +///////////////////////////////////////////////////////////////////////////// +/// Initialize platform interface and register with smi() + +void smart_interface::init() +{ + static os::darwin_smart_interface the_interface; + smart_interface::set(&the_interface); +} diff --git a/os_darwin.h b/os_darwin.h new file mode 100644 index 0000000..a6b31a9 --- /dev/null +++ b/os_darwin.h @@ -0,0 +1,80 @@ +/* + * os_generic.h + * + * Home page of code is: http://www.smartmontools.org + * + * Copyright (C) 2004-8 Geoff Keating <geoffk@geoffk.org> + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef OS_DARWIN_H_ +#define OS_DARWIN_H_ + +#define OS_DARWIN_H_CVSID "$Id: os_darwin.h 5073 2020-06-24 08:08:38Z samm2 $\n" + +#define kIOATABlockStorageDeviceClass "IOATABlockStorageDevice" + +// Isn't in 10.3.9? + +#ifndef kIOPropertySMARTCapableKey +#define kIOPropertySMARTCapableKey "SMART Capable" +#endif + +// NVMe definitions based on Xcode SDK, see NVMeSMARTLibExternal.h +#define kIOPropertyNVMeSMARTCapableKey "NVMe SMART Capable" + +// Constant to init driver +#define kIONVMeSMARTUserClientTypeID CFUUIDGetConstantUUIDWithBytes(NULL, \ + 0xAA, 0x0F, 0xA6, 0xF9, 0xC2, 0xD6, 0x45, 0x7F, 0xB1, 0x0B, \ + 0x59, 0xA1, 0x32, 0x53, 0x29, 0x2F) + +// Constant to use plugin interface +#define kIONVMeSMARTInterfaceID CFUUIDGetConstantUUIDWithBytes(NULL, \ + 0xcc, 0xd1, 0xdb, 0x19, 0xfd, 0x9a, 0x4d, 0xaf, 0xbf, 0x95, \ + 0x12, 0x45, 0x4b, 0x23, 0xa, 0xb6) + +typedef struct IONVMeSMARTInterface +{ + IUNKNOWN_C_GUTS; + + UInt16 version; + UInt16 revision; + + // NVMe smart data, returns nvme_smart_log structure + IOReturn ( *SMARTReadData )( void * interface, + struct nvme_smart_log * NVMeSMARTData ); + + // NVMe IdentifyData, returns nvme_id_ctrl per namespace + IOReturn ( *GetIdentifyData )( void * interface, + struct nvme_id_ctrl * NVMeIdentifyControllerStruct, + unsigned int ns ); + UInt64 reserved0; + UInt64 reserved1; + + // NumDWords Number of dwords for log page data, zero based. + IOReturn ( *GetLogPage )( void * interface, void * data, unsigned int logPageId, unsigned int numDWords); + + UInt64 reserved2; + UInt64 reserved3; + UInt64 reserved4; + UInt64 reserved5; + UInt64 reserved6; + UInt64 reserved7; + UInt64 reserved8; + UInt64 reserved9; + UInt64 reserved10; + UInt64 reserved11; + UInt64 reserved12; + UInt64 reserved13; + UInt64 reserved14; + UInt64 reserved15; + UInt64 reserved16; + UInt64 reserved17; + UInt64 reserved18; + UInt64 reserved19; + +} IONVMeSMARTInterface; + + +#endif /* OS_DARWIN_H_ */ diff --git a/os_darwin/com.smartmontools.smartd.plist.in b/os_darwin/com.smartmontools.smartd.plist.in new file mode 100644 index 0000000..c025e45 --- /dev/null +++ b/os_darwin/com.smartmontools.smartd.plist.in @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> + <dict> + <key>KeepAlive</key> + <dict> + <key>SuccessfulExit</key> + <false/> + </dict> + <key>Label</key> + <string>com.smartmontools.smartd</string> + <key>ProgramArguments</key> + <array> + <string>/usr/local/sbin/smartd</string> + <string>-n</string> + </array> + <key>RunAtLoad</key> + <true/> + </dict> +</plist> diff --git a/os_darwin/pkg/Distribution.in b/os_darwin/pkg/Distribution.in new file mode 100644 index 0000000..df3ec9c --- /dev/null +++ b/os_darwin/pkg/Distribution.in @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8" standalone="no"?> +<installer-gui-script minSpecVersion="2"> + <allowed-os-versions> + <os-version min="10.5"/> + </allowed-os-versions> + <license file="license.txt" mime-type="text/plain"/> + <!-- <welcome file="welcome.rtf" mime-type="text/rtf"/> --> + <title>S.M.A.R.T. disk monitoring tools</title> + <pkg-ref id="com.smartmontools.pkg"/> + <choices-outline> + <line choice="default"> + <line choice="com.smartmontools.pkg"/> + </line> + </choices-outline> + <choice id="default"/> + <choice id="com.smartmontools.pkg" visible="false"> + <pkg-ref version="@version@" installKBytes="@size@" id="com.smartmontools.pkg">@pkgname@</pkg-ref> + </choice> + <domains enable_localSystem="true"/> + <options customize="never" rootVolumeOnly="true"/> +</installer-gui-script> diff --git a/os_darwin/pkg/PackageInfo.in b/os_darwin/pkg/PackageInfo.in new file mode 100644 index 0000000..92ea7a2 --- /dev/null +++ b/os_darwin/pkg/PackageInfo.in @@ -0,0 +1,6 @@ +<pkg-info format-version="2" identifier="com.smartmontools.pkg" version="@version@" install-location="/" auth="root"> +<payload installKBytes="@size@" numberOfFiles="@files@"/> +<!-- <scripts> + <postinstall file="./postinstall"/> +</scripts> --> +</pkg-info> diff --git a/os_darwin/pkg/installer/README.html b/os_darwin/pkg/installer/README.html new file mode 100644 index 0000000..6abdc80 --- /dev/null +++ b/os_darwin/pkg/installer/README.html @@ -0,0 +1,26 @@ +<html> +<body> +<h2>About this package</h2> +The smartmontools package contains two utility programs (smartctl and smartd) to control +and monitor storage systems using the Self-Monitoring, Analysis and Reporting +Technology System (SMART) built into most modern ATA and SCSI harddisks. +In many cases, these utilities will provide advanced warning of disk degradation and failure. +<h2>Installing</h2> +To install package click on the smartmontools.pkg icon and follow installation process. Files will be installed to the <b>/usr/local/</b> directory. +<h2>Usage</h2> + If you are having trouble understanding the output of smartctl or smartd, please first read the manual pages installed on your system: +<pre> + man 8 smartctl + man 8 smartd + man 8 update-smart-drivedb + man 5 smartd.conf +</pre> +To use smartmontools with USB drives please download and install +<a href="https://github.com/kasbert/OS-X-SAT-SMART-Driver">Max OS X kernel driver for providing access to external drive SMART data</a>. SAT SMART Driver is a free open source project (published under Apple Public Source License) by Jarkko Sonninen. +If you are using OS X El Capitan 10.11+ it is recommended to use signed version available from <a href="http://binaryfruit.com/drivedx/usb-drive-support/">DriveDx web site</a>. +<p> +More information could be found on the <a href="https://www.smartmontools.org">www.smartmontools.org</a> website. +<h2>Uninstalling</h2> +If you want to uninstall already installed package run <tt>'sudo smart-pkg-uninstall'</tt> in the terminal. +</body> +</html> diff --git a/os_darwin/pkg/root/usr/local/sbin/smart-pkg-uninstall b/os_darwin/pkg/root/usr/local/sbin/smart-pkg-uninstall new file mode 100755 index 0000000..72bb3fa --- /dev/null +++ b/os_darwin/pkg/root/usr/local/sbin/smart-pkg-uninstall @@ -0,0 +1,40 @@ +#!/bin/sh + +echo "Smartmontools package uninstaller:" + +# check if we are running with root uid +if [[ $EUID -ne 0 ]]; then + echo " Error: this script must be run as root" + exit 1 +fi + +# check if package is installed +pkgutil --info com.smartmontools.pkg > /dev/null 2>/dev/null +if [ $? -ne 0 ]; then + echo " Error: smartmontools package is not installed" + exit 1 +fi + +# smartmontools pkg could be installed only on system volume, so this should be safe +cd / + +echo " - removing files" +for str in `pkgutil --files com.smartmontools.pkg` +do + if [ -f "$str" ] + then + rm -f "$str" + fi +done +echo " - removing empty directories" +for str in `pkgutil --files com.smartmontools.pkg` +do + if [ -d "$str" ] + then + rmdir -p "$str" 2>/dev/null + fi +done + +echo " - removing package system entry" +pkgutil --forget com.smartmontools.pkg +echo "Done, smartmontolls package removed" |