]> git.proxmox.com Git - mirror_smartmontools-debian.git/blame - os_darwin.cpp
Stop passing arguments to dh_installinit
[mirror_smartmontools-debian.git] / os_darwin.cpp
CommitLineData
832b75ed 1/*
d2e702cf 2 * os_darwin.cpp
832b75ed 3 *
6b80b4d2 4 * Home page of code is: http://www.smartmontools.org
832b75ed 5 *
34ad0c5f 6 * Copyright (C) 2004-8 Geoffrey Keating <geoffk@geoffk.org>
d2e702cf 7 * Copyright (C) 2014 Alex Samorukov <samm@os2.kiev.ua>
832b75ed
GG
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2, or (at your option)
12 * any later version.
13 *
d2e702cf
GI
14 * You should have received a copy of the GNU General Public License
15 * along with smartmontools. If not, see <http://www.gnu.org/licenses/>.
16 *
832b75ed
GG
17 */
18
19#include <stdbool.h>
20#include <errno.h>
a37e7145 21#include <unistd.h>
832b75ed
GG
22#include <mach/mach.h>
23#include <mach/mach_error.h>
24#include <mach/mach_init.h>
6b80b4d2 25#include <sys/utsname.h>
832b75ed
GG
26#include <IOKit/IOCFPlugIn.h>
27#include <IOKit/IOKitLib.h>
28#include <IOKit/IOReturn.h>
29#include <IOKit/IOBSD.h>
4d59bff9 30#include <IOKit/storage/IOBlockStorageDevice.h>
832b75ed
GG
31#include <IOKit/storage/IOStorageDeviceCharacteristics.h>
32#include <IOKit/storage/IOMedia.h>
4d59bff9
GG
33#include <IOKit/storage/ata/IOATAStorageDefines.h>
34#include <IOKit/storage/ata/ATASMARTLib.h>
832b75ed
GG
35#include <CoreFoundation/CoreFoundation.h>
36
37 // No, I don't know why there isn't a header for this.
38#define kIOATABlockStorageDeviceClass "IOATABlockStorageDevice"
39
40#include "config.h"
41#include "int64.h"
42#include "atacmds.h"
43#include "scsicmds.h"
44#include "utility.h"
832b75ed 45#include "os_darwin.h"
d2e702cf 46#include "dev_interface.h"
832b75ed
GG
47
48// Needed by '-V' option (CVS versioning) of smartd/smartctl
6b80b4d2 49const char *os_darwin_cpp_cvsid="$Id: os_darwin.cpp 4214 2016-01-24 22:53:37Z samm2 $" \
832b75ed
GG
50ATACMDS_H_CVSID CONFIG_H_CVSID INT64_H_CVSID OS_DARWIN_H_CVSID SCSICMDS_H_CVSID UTILITY_H_CVSID;
51
d2e702cf
GI
52// examples for smartctl
53static const char smartctl_examples[] =
54 "=================================================== SMARTCTL EXAMPLES =====\n\n"
832b75ed
GG
55 " smartctl -a disk0 (Prints all SMART information)\n\n"
56 " smartctl -t long /dev/disk0 (Executes extended disk self-test)\n\n"
57#ifdef HAVE_GETOPT_LONG
58 " smartctl --smart=on --saveauto=on /dev/rdisk0 (Enables SMART on first disk)\n\n"
59 " smartctl --attributes --log=selftest --quietmode=errorsonly /dev/disk0\n"
60 " (Prints Self-Test & Attribute errors)\n\n"
61#else
62 " smartctl -s on -S on /dev/rdisk0 (Enables SMART on first disk)\n\n"
63 " smartctl -A -l selftest -q errorsonly /dev/disk0\n"
64 " (Prints Self-Test & Attribute errors)\n\n"
65#endif
66 " smartctl -a IOService:/MacRISC2PE/pci@f4000000/AppleMacRiscPCI/ata-6@D/AppleKauaiATA/ATADeviceNub@0/IOATABlockStorageDriver/IOATABlockStorageDevice\n"
67 " (You can use IOService: ...)\n\n"
68 " smartctl -c IODeviceTree:/pci@f4000000/ata-6@D/@0:0\n"
69 " (... Or IODeviceTree:)\n"
d2e702cf
GI
70 ;
71
72
73// Information that we keep about each device.
74
75static struct {
76 io_object_t ioob;
77 IOCFPlugInInterface **plugin;
78 IOATASMARTInterface **smartIf;
79} devices[20];
80
6b80b4d2 81const char * dev_darwin_cpp_cvsid = "$Id: os_darwin.cpp 4214 2016-01-24 22:53:37Z samm2 $"
d2e702cf
GI
82 DEV_INTERFACE_H_CVSID;
83
84/////////////////////////////////////////////////////////////////////////////
85
86namespace os { // No need to publish anything, name provided for Doxygen
87
88/////////////////////////////////////////////////////////////////////////////
89/// Implement shared open/close routines with old functions.
90
91class darwin_smart_device
92: virtual public /*implements*/ smart_device
93{
94public:
95 explicit darwin_smart_device(const char * mode)
96 : smart_device(never_called),
97 m_fd(-1), m_mode(mode) { }
98
99 virtual ~darwin_smart_device() throw();
100
101 virtual bool is_open() const;
102
103 virtual bool open();
104
105 virtual bool close();
106
107protected:
108 /// Return filedesc for derived classes.
109 int get_fd() const
110 { return m_fd; }
111
112
113private:
114 int m_fd; ///< filedesc, -1 if not open.
115 const char * m_mode; ///< Mode string for deviceopen().
116};
117
118
119darwin_smart_device::~darwin_smart_device() throw()
120{
121 if (m_fd >= 0)
122 darwin_smart_device::close();
832b75ed
GG
123}
124
d2e702cf
GI
125bool darwin_smart_device::is_open() const
126{
127 return (m_fd >= 0);
832b75ed
GG
128}
129
4d59bff9
GG
130// Determine whether 'dev' is a SMART-capable device.
131static bool is_smart_capable (io_object_t dev) {
132 CFTypeRef smartCapableKey;
133 CFDictionaryRef diskChars;
134
135 // If the device has kIOPropertySMARTCapableKey, then it's capable,
136 // no matter what it looks like.
137 smartCapableKey = IORegistryEntryCreateCFProperty
138 (dev, CFSTR (kIOPropertySMARTCapableKey),
139 kCFAllocatorDefault, 0);
140 if (smartCapableKey)
141 {
142 CFRelease (smartCapableKey);
143 return true;
144 }
145
146 // If it's an kIOATABlockStorageDeviceClass then we're successful
147 // only if its ATA features indicate it supports SMART.
148 if (IOObjectConformsTo (dev, kIOATABlockStorageDeviceClass)
d2e702cf
GI
149 && (diskChars = (CFDictionaryRef)IORegistryEntryCreateCFProperty
150 (dev, CFSTR (kIOPropertyDeviceCharacteristicsKey),
151 kCFAllocatorDefault, kNilOptions)) != NULL)
4d59bff9
GG
152 {
153 CFNumberRef diskFeatures = NULL;
154 UInt32 ataFeatures = 0;
155
156 if (CFDictionaryGetValueIfPresent (diskChars, CFSTR ("ATA Features"),
d2e702cf
GI
157 (const void **)&diskFeatures))
158 CFNumberGetValue (diskFeatures, kCFNumberLongType,
159 &ataFeatures);
4d59bff9
GG
160 CFRelease (diskChars);
161 if (diskFeatures)
d2e702cf 162 CFRelease (diskFeatures);
4d59bff9
GG
163
164 return (ataFeatures & kIOATAFeatureSMART) != 0;
165 }
166 return false;
167}
168
d2e702cf
GI
169bool darwin_smart_device::open()
170{
171 // Acceptable device names are:
172 // /dev/disk*
173 // /dev/rdisk*
174 // disk*
175 // IOService:*
176 // IODeviceTree:*
832b75ed
GG
177 size_t devnum;
178 const char *devname;
179 io_object_t disk;
d2e702cf
GI
180 const char *pathname = get_dev_name();
181 char *type = const_cast<char*>(m_mode);
832b75ed
GG
182
183 if (strcmp (type, "ATA") != 0)
184 {
6b80b4d2
JD
185 set_err (EINVAL);
186 return false;
832b75ed
GG
187 }
188
189 // Find a free device number.
190 for (devnum = 0; devnum < sizeof (devices) / sizeof (devices[0]); devnum++)
191 if (! devices[devnum].ioob)
192 break;
193 if (devnum == sizeof (devices) / sizeof (devices[0]))
194 {
6b80b4d2
JD
195 set_err (EMFILE);
196 return false;
832b75ed
GG
197 }
198
199 devname = NULL;
200 if (strncmp (pathname, "/dev/rdisk", 10) == 0)
201 devname = pathname + 6;
202 else if (strncmp (pathname, "/dev/disk", 9) == 0)
203 devname = pathname + 5;
204 else if (strncmp (pathname, "disk", 4) == 0)
205 // allow user to just say 'disk0'
206 devname = pathname;
207
208 // Find the device.
209 if (devname)
210 {
211 CFMutableDictionaryRef matcher;
212 matcher = IOBSDNameMatching (kIOMasterPortDefault, 0, devname);
213 disk = IOServiceGetMatchingService (kIOMasterPortDefault, matcher);
214 }
215 else
216 {
217 disk = IORegistryEntryFromPath (kIOMasterPortDefault, pathname);
218 }
219
220 if (! disk)
221 {
6b80b4d2
JD
222 set_err(ENOENT);
223 return false;
832b75ed
GG
224 }
225
4d59bff9
GG
226 // Find a SMART-capable driver which is a parent of this device.
227 while (! is_smart_capable (disk))
832b75ed
GG
228 {
229 IOReturn err;
4d59bff9 230 io_object_t prevdisk = disk;
832b75ed 231
4d59bff9
GG
232 // Find this device's parent and try again.
233 err = IORegistryEntryGetParentEntry (disk, kIOServicePlane, &disk);
832b75ed 234 if (err != kIOReturnSuccess || ! disk)
d2e702cf 235 {
6b80b4d2 236 set_err(ENODEV);
d2e702cf 237 IOObjectRelease (prevdisk);
6b80b4d2 238 return false;
d2e702cf 239 }
832b75ed 240 }
832b75ed 241
4d59bff9
GG
242 devices[devnum].ioob = disk;
243
832b75ed
GG
244 {
245 SInt32 dummy;
246
247 devices[devnum].plugin = NULL;
248 devices[devnum].smartIf = NULL;
249
250 // Create an interface to the ATA SMART library.
4d59bff9 251 if (IOCreatePlugInInterfaceForService (disk,
d2e702cf
GI
252 kIOATASMARTUserClientTypeID,
253 kIOCFPlugInInterfaceID,
254 &devices[devnum].plugin,
255 &dummy) == kIOReturnSuccess)
256 (*devices[devnum].plugin)->QueryInterface
257 (devices[devnum].plugin,
258 CFUUIDGetUUIDBytes ( kIOATASMARTInterfaceID),
259 (void **)&devices[devnum].smartIf);
832b75ed 260 }
6b80b4d2
JD
261
262
d2e702cf
GI
263 m_fd = devnum;
264 if (m_fd < 0) {
265 set_err((errno==ENOENT || errno==ENOTDIR) ? ENODEV : errno);
266 return false;
267 }
268 return true;
832b75ed
GG
269}
270
d2e702cf
GI
271bool darwin_smart_device::close()
272{
273 int fd = m_fd; m_fd = -1;
832b75ed
GG
274 if (devices[fd].smartIf)
275 (*devices[fd].smartIf)->Release (devices[fd].smartIf);
276 if (devices[fd].plugin)
277 IODestroyPlugInInterface (devices[fd].plugin);
278 IOObjectRelease (devices[fd].ioob);
279 devices[fd].ioob = MACH_PORT_NULL;
d2e702cf
GI
280 return true;
281}
282
283// makes a list of ATA or SCSI devices for the DEVICESCAN directive of
284// smartd. Returns number N of devices, or -1 if out of
285// memory. Allocates N+1 arrays: one of N pointers (devlist); the
286// other N arrays each contain null-terminated character strings. In
287// the case N==0, no arrays are allocated because the array of 0
288// pointers has zero length, equivalent to calling malloc(0).
289static int make_device_names (char*** devlist, const char* name) {
290 IOReturn err;
291 io_iterator_t i;
292 io_object_t device = MACH_PORT_NULL;
293 int result;
294 int index;
295
296 // We treat all devices as ATA so long as they support SMARTLib.
297 if (strcmp (name, "ATA") != 0)
298 return 0;
299
300 err = IOServiceGetMatchingServices
301 (kIOMasterPortDefault, IOServiceMatching (kIOBlockStorageDeviceClass), &i);
302 if (err != kIOReturnSuccess)
303 return -1;
304
305 // Count the devices.
306 result = 0;
307 while ((device = IOIteratorNext (i)) != MACH_PORT_NULL) {
308 if (is_smart_capable (device))
309 result++;
310 IOObjectRelease (device);
311 }
312
313 // Create an array of service names.
314 IOIteratorReset (i);
6b80b4d2 315 if (! result)
d2e702cf 316 goto error;
6b80b4d2 317 *devlist = (char**)calloc (result, sizeof (char *));
d2e702cf
GI
318 index = 0;
319 while ((device = IOIteratorNext (i)) != MACH_PORT_NULL) {
320 if (is_smart_capable (device))
321 {
322 io_string_t devName;
323 IORegistryEntryGetPath(device, kIOServicePlane, devName);
324 (*devlist)[index] = strdup (devName);
325 if (! (*devlist)[index])
326 goto error;
327 index++;
328 }
329 IOObjectRelease (device);
330 }
331
332 IOObjectRelease (i);
333 return result;
334
335 error:
336 if (device != MACH_PORT_NULL)
337 IOObjectRelease (device);
338 IOObjectRelease (i);
339 if (*devlist)
340 {
341 for (index = 0; index < result; index++)
342 if ((*devlist)[index])
343 free ((*devlist)[index]);
344 free (*devlist);
345 }
6b80b4d2
JD
346 if(!result) // no devs found
347 return 0;
348
d2e702cf
GI
349 return -1;
350}
351
352/////////////////////////////////////////////////////////////////////////////
353/// Implement standard ATA support
354
355class darwin_ata_device
356: public /*implements*/ ata_device,
357 public /*extends*/ darwin_smart_device
358{
359public:
360 darwin_ata_device(smart_interface * intf, const char * dev_name, const char * req_type);
361 virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out);
362
363protected:
364 // virtual int ata_command_interface(smart_command_set command, int select, char * data);
365};
366
367darwin_ata_device::darwin_ata_device(smart_interface * intf, const char * dev_name, const char * req_type)
368: smart_device(intf, dev_name, "ata", req_type),
369 darwin_smart_device("ATA")
370{
832b75ed
GG
371}
372
d2e702cf 373bool darwin_ata_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out)
832b75ed 374{
d2e702cf
GI
375 if (!ata_cmd_is_ok(in,
376 true, // data_out_support
377 true, // multi_sector_support
378 false) // not supported by API
379 )
380 return false;
381
382 int select = 0;
383 char * data = (char *)in.buffer;
384 int fd = get_fd();
832b75ed
GG
385 IOATASMARTInterface **ifp = devices[fd].smartIf;
386 IOATASMARTInterface *smartIf;
387 IOReturn err;
a37e7145 388 int timeoutCount = 5;
d2e702cf 389 int rc = 0;
832b75ed
GG
390
391 if (! ifp)
392 return -1;
393 smartIf = *ifp;
d2e702cf 394 clear_err(); errno = 0;
4d59bff9 395 do {
d2e702cf
GI
396 switch (in.in_regs.command) {
397 case ATA_IDENTIFY_DEVICE:
832b75ed 398 {
d2e702cf
GI
399 UInt32 dummy;
400 err = smartIf->GetATAIdentifyData (ifp, data, 512, &dummy);
401 if (err != kIOReturnSuccess && err != kIOReturnTimeout
402 && err != kIOReturnNotResponding)
403 printf ("identify failed: %#x\n", (unsigned) rc);
404 if (err == kIOReturnSuccess && isbigendian())
405 {
406 int i;
407 /* The system has already byte-swapped, undo it. */
408 for (i = 0; i < 256; i+=2)
409 swap2 (data + i);
410 }
411 }
412 break;
413 case ATA_IDENTIFY_PACKET_DEVICE:
414 case ATA_CHECK_POWER_MODE:
415 errno = ENOTSUP;
416 err = -1;
417 break;
418 case ATA_SMART_CMD:
419 switch (in.in_regs.features) {
420 case ATA_SMART_READ_VALUES:
421 err = smartIf->SMARTReadData (ifp, (ATASMARTData *)data);
422 break;
423 case ATA_SMART_READ_THRESHOLDS:
424 err = smartIf->SMARTReadDataThresholds (ifp,
425 (ATASMARTDataThresholds *)data);
426 break;
427 case ATA_SMART_READ_LOG_SECTOR:
428 err = smartIf->SMARTReadLogAtAddress (ifp, in.in_regs.lba_low, data, 512 * in.in_regs.sector_count);
429 break;
430 case ATA_SMART_WRITE_LOG_SECTOR:
431 err = smartIf->SMARTWriteLogAtAddress (ifp, in.in_regs.lba_low, data, 512 * in.in_regs.sector_count);
432 break;
433 case ATA_SMART_ENABLE:
434 case ATA_SMART_DISABLE:
435 err = smartIf->SMARTEnableDisableOperations (ifp, in.in_regs.features == ATA_SMART_ENABLE);
436 break;
437 case ATA_SMART_STATUS:
438 if (in.out_needed.lba_high) // statuscheck
439 {
440 Boolean is_failing;
441 err = smartIf->SMARTReturnStatus (ifp, &is_failing);
442 if (err == kIOReturnSuccess && is_failing) {
443 err = -1; // thresholds exceeded condition
444 out.out_regs.lba_high = 0x2c; out.out_regs.lba_mid = 0xf4;
445 }
446 else
447 out.out_regs.lba_high = 0xc2; out.out_regs.lba_mid = 0x4f;
448 break;
449 }
450 else err = 0;
451 break;
452 case ATA_SMART_AUTOSAVE:
453 err = smartIf->SMARTEnableDisableAutosave (ifp,
454 (in.in_regs.sector_count == 241 ? true : false));
455 break;
456 case ATA_SMART_IMMEDIATE_OFFLINE:
457 select = in.in_regs.lba_low;
458 if (select != SHORT_SELF_TEST && select != EXTEND_SELF_TEST)
459 {
460 errno = EINVAL;
6b80b4d2 461 return set_err(ENOSYS, "Unsupported SMART self-test mode");
d2e702cf
GI
462 }
463 err = smartIf->SMARTExecuteOffLineImmediate (ifp,
464 select == EXTEND_SELF_TEST);
465 break;
466 case ATA_SMART_AUTO_OFFLINE:
467 return set_err(ENOSYS, "SMART command not supported");
4d59bff9 468 default:
d2e702cf 469 return set_err(ENOSYS, "Unknown SMART command");
832b75ed 470 }
d2e702cf
GI
471 break;
472 default:
473 return set_err(ENOSYS, "Non-SMART commands not implemented");
474 }
a37e7145 475 } while ((err == kIOReturnTimeout || err == kIOReturnNotResponding)
d2e702cf 476 && timeoutCount-- > 0);
832b75ed
GG
477 if (err == kIOReturnExclusiveAccess)
478 errno = EBUSY;
d2e702cf
GI
479 rc = err == kIOReturnSuccess ? 0 : -1;
480 if (rc < 0) {
481 if (!get_errno())
482 set_err(errno);
483 return false;
484 }
485 return true;
486}
487
488/////////////////////////////////////////////////////////////////////////////
489/// Implement platform interface
490
491class darwin_smart_interface
492: public /*implements*/ smart_interface
493{
494public:
6b80b4d2
JD
495 virtual std::string get_os_version_str();
496
d2e702cf
GI
497 virtual std::string get_app_examples(const char * appname);
498
499 virtual bool scan_smart_devices(smart_device_list & devlist, const char * type,
500 const char * pattern = 0);
501
502protected:
503 virtual ata_device * get_ata_device(const char * name, const char * type);
504
505 virtual scsi_device * get_scsi_device(const char * name, const char * type);
506
507 virtual smart_device * autodetect_smart_device(const char * name);
508
509};
510
511
512//////////////////////////////////////////////////////////////////////
513
6b80b4d2
JD
514std::string darwin_smart_interface::get_os_version_str()
515{
516 // now we are just getting darwin runtime version, to get OSX version more things needs to be done, see
517 // http://stackoverflow.com/questions/11072804/how-do-i-determine-the-os-version-at-runtime-in-os-x-or-ios-without-using-gesta
518 struct utsname osname;
519 uname(&osname);
520 return strprintf("%s %s %s", osname.sysname, osname.release, osname.machine);
521}
522
d2e702cf
GI
523std::string darwin_smart_interface::get_app_examples(const char * appname)
524{
525 if (!strcmp(appname, "smartctl"))
526 return smartctl_examples;
527 return ""; // ... so don't print again.
832b75ed
GG
528}
529
d2e702cf
GI
530ata_device * darwin_smart_interface::get_ata_device(const char * name, const char * type)
531{
532 return new darwin_ata_device(this, name, type);
533}
534
535scsi_device * darwin_smart_interface::get_scsi_device(const char *, const char *)
536{
537 return 0; // scsi devices are not supported [yet]
538}
539
540
541smart_device * darwin_smart_interface::autodetect_smart_device(const char * name)
542{
543 return new darwin_ata_device(this, name, "");
544}
545
546static void free_devnames(char * * devnames, int numdevs)
547{
548 for (int i = 0; i < numdevs; i++)
549 free(devnames[i]);
550 free(devnames);
551}
552
553bool darwin_smart_interface::scan_smart_devices(smart_device_list & devlist,
554 const char * type, const char * pattern /*= 0*/)
555{
556 if (pattern) {
557 set_err(EINVAL, "DEVICESCAN with pattern not implemented yet");
558 return false;
559 }
560
561 // Make namelists
562 char * * atanames = 0; int numata = 0;
563 if (!type || !strcmp(type, "ata")) {
564 numata = make_device_names(&atanames, "ATA");
565 if (numata < 0) {
566 set_err(ENOMEM);
567 return false;
568 }
569 }
570
571 // Add to devlist
572 int i;
573 if (!type)
574 type="";
575 for (i = 0; i < numata; i++) {
576 ata_device * atadev = get_ata_device(atanames[i], type);
577 if (atadev)
578 devlist.push_back(atadev);
579 }
580 free_devnames(atanames, numata);
581 return true;
582}
583
584} // namespace
585
586
587/////////////////////////////////////////////////////////////////////////////
588/// Initialize platform interface and register with smi()
589
590void smart_interface::init()
591{
592 static os::darwin_smart_interface the_interface;
593 smart_interface::set(&the_interface);
832b75ed 594}