Annotation of embedaddon/smartmontools/os_darwin.cpp, revision 1.1.1.3
1.1 misho 1: /*
2: * os_darwin.c
3: *
4: * Home page of code is: http://smartmontools.sourceforge.net
5: *
6: * Copyright (C) 2004-8 Geoffrey Keating <geoffk@geoffk.org>
7: *
8: * This program is free software; you can redistribute it and/or modify
9: * it under the terms of the GNU General Public License as published by
10: * the Free Software Foundation; either version 2, or (at your option)
11: * any later version.
12: *
13: * You should have received a copy of the GNU General Public License
1.1.1.2 misho 14: * (for example COPYING); if not, write to the Free Software Foundation,
15: * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
1.1 misho 16: */
17:
18: #include <stdbool.h>
19: #include <errno.h>
20: #include <unistd.h>
21: #include <mach/mach.h>
22: #include <mach/mach_error.h>
23: #include <mach/mach_init.h>
24: #include <IOKit/IOCFPlugIn.h>
25: #include <IOKit/IOKitLib.h>
26: #include <IOKit/IOReturn.h>
27: #include <IOKit/IOBSD.h>
28: #include <IOKit/storage/IOBlockStorageDevice.h>
29: #include <IOKit/storage/IOStorageDeviceCharacteristics.h>
30: #include <IOKit/storage/IOMedia.h>
31: #include <IOKit/storage/ata/IOATAStorageDefines.h>
32: #include <IOKit/storage/ata/ATASMARTLib.h>
33: #include <CoreFoundation/CoreFoundation.h>
34:
35: // No, I don't know why there isn't a header for this.
36: #define kIOATABlockStorageDeviceClass "IOATABlockStorageDevice"
37:
38: #include "config.h"
39: #include "int64.h"
40: #include "atacmds.h"
41: #include "scsicmds.h"
42: #include "utility.h"
43:
44: #include "os_darwin.h"
45:
46: // Needed by '-V' option (CVS versioning) of smartd/smartctl
1.1.1.3 ! misho 47: const char *os_XXXX_c_cvsid="$Id: os_darwin.cpp 3805 2013-03-29 19:54:18Z chrfranke $" \
1.1 misho 48: ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_DARWIN_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
49:
50: // Print examples for smartctl.
51: void print_smartctl_examples(){
52: printf("=================================================== SMARTCTL EXAMPLES =====\n\n");
53: printf(
54: " smartctl -a disk0 (Prints all SMART information)\n\n"
55: " smartctl -t long /dev/disk0 (Executes extended disk self-test)\n\n"
56: #ifdef HAVE_GETOPT_LONG
57: " smartctl --smart=on --saveauto=on /dev/rdisk0 (Enables SMART on first disk)\n\n"
58: " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/disk0\n"
59: " (Prints Self-Test & Attribute errors)\n\n"
60: #else
61: " smartctl -s on -S on /dev/rdisk0 (Enables SMART on first disk)\n\n"
62: " smartctl -A -l selftest -q errorsonly /dev/disk0\n"
63: " (Prints Self-Test & Attribute errors)\n\n"
64: #endif
65: " smartctl -a IOService:/MacRISC2PE/pci@f4000000/AppleMacRiscPCI/ata-6@D/AppleKauaiATA/ATADeviceNub@0/IOATABlockStorageDriver/IOATABlockStorageDevice\n"
66: " (You can use IOService: ...)\n\n"
67: " smartctl -c IODeviceTree:/pci@f4000000/ata-6@D/@0:0\n"
68: " (... Or IODeviceTree:)\n"
69: );
70: return;
71: }
72:
73: // tries to guess device type given the name (a path). See utility.h
74: // for return values.
1.1.1.3 ! misho 75: int guess_device_type (const char * /* dev_name */) {
1.1 misho 76: // Only ATA is supported right now, so that's what it'd better be.
77: return CONTROLLER_ATA;
78: }
79:
80: // Determine whether 'dev' is a SMART-capable device.
81: static bool is_smart_capable (io_object_t dev) {
82: CFTypeRef smartCapableKey;
83: CFDictionaryRef diskChars;
84:
85: // If the device has kIOPropertySMARTCapableKey, then it's capable,
86: // no matter what it looks like.
87: smartCapableKey = IORegistryEntryCreateCFProperty
88: (dev, CFSTR (kIOPropertySMARTCapableKey),
89: kCFAllocatorDefault, 0);
90: if (smartCapableKey)
91: {
92: CFRelease (smartCapableKey);
93: return true;
94: }
95:
96: // If it's an kIOATABlockStorageDeviceClass then we're successful
97: // only if its ATA features indicate it supports SMART.
98: if (IOObjectConformsTo (dev, kIOATABlockStorageDeviceClass)
99: && (diskChars = (CFDictionaryRef)IORegistryEntryCreateCFProperty
100: (dev, CFSTR (kIOPropertyDeviceCharacteristicsKey),
101: kCFAllocatorDefault, kNilOptions)) != NULL)
102: {
103: CFNumberRef diskFeatures = NULL;
104: UInt32 ataFeatures = 0;
105:
106: if (CFDictionaryGetValueIfPresent (diskChars, CFSTR ("ATA Features"),
107: (const void **)&diskFeatures))
108: CFNumberGetValue (diskFeatures, kCFNumberLongType,
109: &ataFeatures);
110: CFRelease (diskChars);
111: if (diskFeatures)
112: CFRelease (diskFeatures);
113:
114: return (ataFeatures & kIOATAFeatureSMART) != 0;
115: }
116: return false;
117: }
118:
119:
120: // makes a list of ATA or SCSI devices for the DEVICESCAN directive of
121: // smartd. Returns number N of devices, or -1 if out of
122: // memory. Allocates N+1 arrays: one of N pointers (devlist); the
123: // other N arrays each contain null-terminated character strings. In
124: // the case N==0, no arrays are allocated because the array of 0
125: // pointers has zero length, equivalent to calling malloc(0).
126: int make_device_names (char*** devlist, const char* name) {
127: IOReturn err;
128: io_iterator_t i;
129: io_object_t device = MACH_PORT_NULL;
130: int result;
131: int index;
132:
133: // We treat all devices as ATA so long as they support SMARTLib.
134: if (strcmp (name, "ATA") != 0)
135: return 0;
136:
137: err = IOServiceGetMatchingServices
138: (kIOMasterPortDefault, IOServiceMatching (kIOBlockStorageDeviceClass), &i);
139: if (err != kIOReturnSuccess)
140: return -1;
141:
142: // Count the devices.
143: result = 0;
144: while ((device = IOIteratorNext (i)) != MACH_PORT_NULL) {
145: if (is_smart_capable (device))
146: result++;
147: IOObjectRelease (device);
148: }
149:
150: // Create an array of service names.
151: IOIteratorReset (i);
152: *devlist = (char**)Calloc (result, sizeof (char *));
153: if (! *devlist)
154: goto error;
155: index = 0;
156: while ((device = IOIteratorNext (i)) != MACH_PORT_NULL) {
157: if (is_smart_capable (device))
158: {
159: io_string_t devName;
160: IORegistryEntryGetPath(device, kIOServicePlane, devName);
161: (*devlist)[index] = CustomStrDup (devName, true, __LINE__, __FILE__);
162: if (! (*devlist)[index])
163: goto error;
164: index++;
165: }
166: IOObjectRelease (device);
167: }
168:
169: IOObjectRelease (i);
170: return result;
171:
172: error:
173: if (device != MACH_PORT_NULL)
174: IOObjectRelease (device);
175: IOObjectRelease (i);
176: if (*devlist)
177: {
178: for (index = 0; index < result; index++)
179: if ((*devlist)[index])
180: FreeNonZero ((*devlist)[index], 0, __LINE__, __FILE__);
181: FreeNonZero (*devlist, result * sizeof (char *), __LINE__, __FILE__);
182: }
183: return -1;
184: }
185:
186: // Information that we keep about each device.
187:
188: static struct {
189: io_object_t ioob;
190: IOCFPlugInInterface **plugin;
191: IOATASMARTInterface **smartIf;
192: } devices[20];
193:
194: // Like open(). Return non-negative integer handle, only used by the
195: // functions below. type=="ATA" or "SCSI". The return value is
196: // an index into the devices[] array. If the device can't be opened,
197: // sets errno and returns -1.
198: // Acceptable device names are:
199: // /dev/disk*
200: // /dev/rdisk*
201: // disk*
202: // IOService:*
203: // IODeviceTree:*
204: int deviceopen(const char *pathname, char *type){
205: size_t devnum;
206: const char *devname;
207: io_object_t disk;
208:
209: if (strcmp (type, "ATA") != 0)
210: {
211: errno = EINVAL;
212: return -1;
213: }
214:
215: // Find a free device number.
216: for (devnum = 0; devnum < sizeof (devices) / sizeof (devices[0]); devnum++)
217: if (! devices[devnum].ioob)
218: break;
219: if (devnum == sizeof (devices) / sizeof (devices[0]))
220: {
221: errno = EMFILE;
222: return -1;
223: }
224:
225: devname = NULL;
226: if (strncmp (pathname, "/dev/rdisk", 10) == 0)
227: devname = pathname + 6;
228: else if (strncmp (pathname, "/dev/disk", 9) == 0)
229: devname = pathname + 5;
230: else if (strncmp (pathname, "disk", 4) == 0)
231: // allow user to just say 'disk0'
232: devname = pathname;
233:
234: // Find the device.
235: if (devname)
236: {
237: CFMutableDictionaryRef matcher;
238: matcher = IOBSDNameMatching (kIOMasterPortDefault, 0, devname);
239: disk = IOServiceGetMatchingService (kIOMasterPortDefault, matcher);
240: }
241: else
242: {
243: disk = IORegistryEntryFromPath (kIOMasterPortDefault, pathname);
244: }
245:
246: if (! disk)
247: {
248: errno = ENOENT;
249: return -1;
250: }
251:
252: // Find a SMART-capable driver which is a parent of this device.
253: while (! is_smart_capable (disk))
254: {
255: IOReturn err;
256: io_object_t prevdisk = disk;
257:
258: // Find this device's parent and try again.
259: err = IORegistryEntryGetParentEntry (disk, kIOServicePlane, &disk);
260: if (err != kIOReturnSuccess || ! disk)
261: {
262: errno = ENODEV;
263: IOObjectRelease (prevdisk);
264: return -1;
265: }
266: }
267:
268: devices[devnum].ioob = disk;
269:
270: {
271: SInt32 dummy;
272:
273: devices[devnum].plugin = NULL;
274: devices[devnum].smartIf = NULL;
275:
276: // Create an interface to the ATA SMART library.
277: if (IOCreatePlugInInterfaceForService (disk,
278: kIOATASMARTUserClientTypeID,
279: kIOCFPlugInInterfaceID,
280: &devices[devnum].plugin,
281: &dummy) == kIOReturnSuccess)
282: (*devices[devnum].plugin)->QueryInterface
283: (devices[devnum].plugin,
284: CFUUIDGetUUIDBytes ( kIOATASMARTInterfaceID),
285: (void **)&devices[devnum].smartIf);
286: }
287:
288: return devnum;
289: }
290:
291: // Like close(). Acts only on integer handles returned by
292: // deviceopen() above.
293: int deviceclose(int fd){
294: if (devices[fd].smartIf)
295: (*devices[fd].smartIf)->Release (devices[fd].smartIf);
296: if (devices[fd].plugin)
297: IODestroyPlugInInterface (devices[fd].plugin);
298: IOObjectRelease (devices[fd].ioob);
299: devices[fd].ioob = MACH_PORT_NULL;
300: return 0;
301: }
302:
303: // Interface to ATA devices. See os_linux.cpp for the cannonical example.
304: // DETAILED DESCRIPTION OF ARGUMENTS
305: // device: is the integer handle provided by deviceopen()
306: // command: defines the different operations, see atacmds.h
307: // select: additional input data IF NEEDED (which log, which type of
308: // self-test).
309: // data: location to write output data, IF NEEDED (1 or 512 bytes).
310: // Note: not all commands use all arguments.
311: // RETURN VALUES (for all commands BUT command==STATUS_CHECK)
312: // -1 if the command failed
313: // 0 if the command succeeded,
314: // RETURN VALUES if command==STATUS_CHECK
315: // -1 if the command failed OR the disk SMART status can't be determined
316: // 0 if the command succeeded and disk SMART status is "OK"
317: // 1 if the command succeeded and disk SMART status is "FAILING"
318:
319: // Things that aren't available in the Darwin interfaces:
320: // - Tests other than short and extended (in particular, can't run
321: // an immediate offline test)
322: // - Captive-mode tests, aborting tests
323: // - ability to switch automatic offline testing on or off
324:
325: // Note that some versions of Darwin, at least 7H63 and earlier,
326: // have a buggy library that treats the boolean value in
327: // SMARTEnableDisableOperations, SMARTEnableDisableAutosave, and
328: // SMARTExecuteOffLineImmediate as always being true.
329: int
330: ata_command_interface(int fd, smart_command_set command,
331: int select, char *data)
332: {
333: IOATASMARTInterface **ifp = devices[fd].smartIf;
334: IOATASMARTInterface *smartIf;
335: IOReturn err;
336: int timeoutCount = 5;
337:
338: if (! ifp)
339: return -1;
340: smartIf = *ifp;
341:
342: do {
343: switch (command)
344: {
345: case STATUS:
346: return 0;
347: case STATUS_CHECK:
348: {
349: Boolean is_failing;
350: err = smartIf->SMARTReturnStatus (ifp, &is_failing);
351: if (err == kIOReturnSuccess && is_failing)
352: return 1;
353: break;
354: }
355: case ENABLE:
356: case DISABLE:
357: err = smartIf->SMARTEnableDisableOperations (ifp, command == ENABLE);
358: break;
359: case AUTOSAVE:
360: err = smartIf->SMARTEnableDisableAutosave (ifp, select != 0);
361: break;
362: case IMMEDIATE_OFFLINE:
363: if (select != SHORT_SELF_TEST && select != EXTEND_SELF_TEST)
364: {
365: errno = EINVAL;
366: return -1;
367: }
368: err = smartIf->SMARTExecuteOffLineImmediate (ifp,
369: select == EXTEND_SELF_TEST);
370: break;
371: case READ_VALUES:
372: err = smartIf->SMARTReadData (ifp, (ATASMARTData *)data);
373: break;
374: case READ_THRESHOLDS:
375: err = smartIf->SMARTReadDataThresholds (ifp,
376: (ATASMARTDataThresholds *)data);
377: break;
378: case READ_LOG:
379: err = smartIf->SMARTReadLogAtAddress (ifp, select, data, 512);
380: break;
381: case WRITE_LOG:
382: err = smartIf->SMARTWriteLogAtAddress (ifp, select, data, 512);
383: break;
384: case IDENTIFY:
385: {
386: UInt32 dummy;
387: err = smartIf->GetATAIdentifyData (ifp, data, 512, &dummy);
388: if (err != kIOReturnSuccess && err != kIOReturnTimeout
389: && err != kIOReturnNotResponding)
390: printf ("identify failed: %#x\n", (unsigned) err);
391: if (err == kIOReturnSuccess && isbigendian())
392: {
393: int i;
394: /* The system has already byte-swapped, undo it. */
395: for (i = 0; i < 256; i+=2)
396: swap2 (data + i);
397: }
398: }
399: break;
400: case CHECK_POWER_MODE:
401: // The information is right there in the device registry, but how
402: // to get to it portably?
403: default:
404: errno = ENOTSUP;
405: return -1;
406: }
407: /* This bit is a bit strange. Apparently, when the drive is spun
408: down, the intended behaviour of these calls is that they fail,
409: return kIOReturnTimeout and then power the drive up. So if
410: you get a timeout, you have to try again to get the actual
411: command run, but the drive is already powering up so you can't
412: use this for CHECK_POWER_MODE. */
413: if (err == kIOReturnTimeout || err == kIOReturnNotResponding)
414: sleep (1);
415: } while ((err == kIOReturnTimeout || err == kIOReturnNotResponding)
416: && timeoutCount-- > 0);
417: if (err == kIOReturnExclusiveAccess)
418: errno = EBUSY;
419: return err == kIOReturnSuccess ? 0 : -1;
420: }
421:
422: // Interface to SCSI devices. See os_linux.c
1.1.1.3 ! misho 423: int do_scsi_cmnd_io(int /* fd */, struct scsi_cmnd_io * /* iop */, int /* report */) {
1.1 misho 424: return -ENOSYS;
425: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>