1/* 2 * Copyright (c) 2004-2007,2011 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24 25// 26// iodevices - code for finding and tracking devices via IOKit 27// 28#include "iodevices.h" 29#include <security_utilities/cfutilities.h> 30#include <IOKit/IOMessage.h> 31#include <IOKit/usb/IOUSBLib.h> 32 33using namespace MachPlusPlus; 34 35 36namespace Security { 37namespace IOKit { 38 39 40// 41// Master Ports. 42// Note that multiple MasterPort objects may refer to the same Mach port. 43// 44MasterPort::MasterPort() 45{ 46 Error::check(::IOMasterPort(MACH_PORT_NULL, &port())); 47} 48 49 50// 51// IOKit Devices (a small subset of functionality) 52// 53Device::~Device() 54{ 55 ::IOObjectRelease(mService); 56} 57 58string Device::name() const 59{ 60 io_name_t tName; 61 Error::check(::IORegistryEntryGetName(mService, tName)); 62 return tName; 63} 64 65string Device::name(const char *plane) const 66{ 67 io_name_t tName; 68 Error::check(::IORegistryEntryGetNameInPlane(mService, plane, tName)); 69 return tName; 70} 71 72string Device::path(const char *plane) const 73{ 74 io_string_t tPath; 75 Error::check(::IORegistryEntryGetPath(mService, plane, tPath)); 76 return tPath; 77} 78 79CFDictionaryRef Device::properties() const 80{ 81 CFMutableDictionaryRef dict; 82 Error::check(::IORegistryEntryCreateCFProperties(mService, &dict, NULL, 0)); 83 return dict; 84} 85 86CFTypeRef Device::property(const char *name) const 87{ 88 return ::IORegistryEntryCreateCFProperty(mService, CFTempString(name), NULL, 0); 89} 90 91 92// 93// DeviceIterators 94// 95DeviceIterator::~DeviceIterator() 96{ 97 // drain the iterator to avoid port leakage 98 while (Device dev = (*this)()) 99 ; 100} 101 102 103io_service_t DeviceIterator::operator () () 104{ 105 io_service_t dev = ::IOIteratorNext(mIterator); 106 mAtEnd = !dev; 107 return dev; 108} 109 110 111// 112// DeviceMatches 113// 114DeviceMatch::DeviceMatch() 115 : CFRef<CFMutableDictionaryRef>(makeCFMutableDictionary()) 116{ 117 CFError::check(*this); 118} 119 120DeviceMatch::DeviceMatch(const char *cls) 121 : CFRef<CFMutableDictionaryRef>(::IOServiceMatching(cls)) 122{ 123 CFError::check(*this); 124} 125 126DeviceMatch::DeviceMatch(const char *cls, const char *name, uint32_t value, ...) 127 : CFRef<CFMutableDictionaryRef>(::IOServiceMatching(cls)) 128{ 129 CFError::check(*this); 130 131 va_list args; 132 va_start(args, value); 133 while (name) { 134 add(name, value); 135 name = va_arg(args, const char *); 136 if (!name) 137 break; 138 value = va_arg(args, uint32_t); 139 } 140} 141 142void DeviceMatch::add(const char *name, uint32_t value) 143{ 144 CFRef<CFNumberRef> number = CFNumberCreate(NULL, kCFNumberSInt32Type, &value); 145 CFDictionarySetValue(*this, CFTempString(name), number); 146} 147 148 149// 150// NotificationPorts 151// 152NotificationPort::NotificationPort() 153{ 154 CFError::check(mPortRef = ::IONotificationPortCreate(MasterPort())); 155} 156 157NotificationPort::NotificationPort(const MasterPort &master) 158{ 159 CFError::check(mPortRef = ::IONotificationPortCreate(master)); 160} 161 162NotificationPort::~NotificationPort() 163{ 164 ::IONotificationPortDestroy(mPortRef); 165} 166 167 168mach_port_t NotificationPort::port() const 169{ 170 mach_port_t p = ::IONotificationPortGetMachPort(mPortRef); 171 CFError::check(p); 172 return p; 173} 174 175CFRunLoopSourceRef NotificationPort::source() const 176{ 177 CFRunLoopSourceRef rls = ::IONotificationPortGetRunLoopSource(mPortRef); 178 CFError::check(rls); 179 return rls; 180} 181 182 183void NotificationPort::add(const DeviceMatch &match, Receiver &receiver, const char *type) 184{ 185 io_iterator_t iterator; 186 CFRetain(match); // compensate for IOSAMN not retaining its argument 187 Error::check(::IOServiceAddMatchingNotification(mPortRef, type, 188 match, 189 ioNotify, &receiver, 190 &iterator)); 191 192 // run initial iterator to process existing devices 193 secdebug("iokit", "dispatching initial device match iterator %d", iterator); 194 DeviceIterator it(iterator); 195 receiver.ioChange(it); 196} 197 198void NotificationPort::addInterestNotification(Receiver &receiver, io_service_t service, 199 const io_name_t interestType) 200{ 201 io_iterator_t iterator; 202 mach_port_t pp = NotificationPort::port(); 203 204 secdebug("iokit", "NotificationPort::addInterest - type: %s [port: %p (0x%08X), service: 0x%08X]", 205 interestType, mPortRef, pp, service); 206 207 // We cannot throw if we get an error here since we will receive notifications 208 // from each plane, and not all planes have the necessary information to be 209 // able to add an interest notification 210 kern_return_t kr = ::IOServiceAddInterestNotification(mPortRef, 211 service, interestType, ioDeviceNotification, &receiver, &iterator); 212 const char *msgstr = mach_error_string(kr); 213 const char *msgtyp = mach_error_type(kr); 214 if (msgstr && msgtyp) 215 secdebug("iokit", " msg: %s, typ: %s", msgstr, msgtyp); 216} 217 218void NotificationPort::ioNotify(void *refCon, io_iterator_t iterator) 219{ 220 secdebug("iokit", "dispatching new device match iterator %d", iterator); 221 DeviceIterator it(iterator); 222 try { 223 reinterpret_cast<Receiver *>(refCon)->ioChange(it); 224 } catch (...) { 225 secdebug("iokit", "ioChange callback threw an exception (ignored)"); 226 } 227} 228 229void NotificationPort::ioDeviceNotification(void *refCon, io_service_t service, 230 natural_t messageType, void *messageArgument) 231{ 232 secdebug("iokit", "dispatching NEW device notification iterator, service 0x%08X, msg: 0x%04X, arg: %p", 233 service, messageType, messageArgument); 234 235 const char *msgstr = mach_error_string(messageType); 236 const char *msgtyp = mach_error_type(messageType); 237 if (msgstr && msgtyp) 238 secdebug("iokit", " msg: %s, typ: %s", msgstr, msgtyp); 239 240 if (service!=io_service_t(-1)) 241 reinterpret_cast<Receiver *>(refCon)->ioServiceChange(refCon, service, messageType, messageArgument); 242} 243 244// 245// Abstract NotificationPort::Receivers 246// 247NotificationPort::Receiver::~Receiver() 248{ /* virtual */ } 249 250 251// 252// MachPortNotificationPorts 253// 254MachPortNotificationPort::MachPortNotificationPort() 255{ 256 NoReplyHandler::port(NotificationPort::port()); 257} 258 259MachPortNotificationPort::~MachPortNotificationPort() 260{ 261} 262 263boolean_t MachPortNotificationPort::handle(mach_msg_header_t *in) 264{ 265 ::IODispatchCalloutFromMessage(NULL, in, mPortRef); 266 return TRUE; 267} 268 269 270 271} // end namespace IOKit 272} // end namespace Security 273