IOCreatePlugInInterfaceForService failed w/ kIOReturnNoResources/0xe00002be
I am rewriting old FireWire based command line utility into XPCService. I need some help about an IOKit function.
Following part is to get IOCFPlugInInterface for FireWireAVCLibUnit.(almost same as original code; basic idea comes from legacy simpleAVC samplecode).
When I call IOCreatePlugInInterfaceForService() in the XPCService, it always failed returning 0xe00002be = kIOReturnNoResources in IOReturn.h.
I have confirmed no sandbox, no hardened for the XPC Service.
Original command line utility works perfectly on macOS 10.14 though, would you someone give me a hint on this topic?
CFDictionaryRef dict = CFDictionaryCreateCopy(kCFAllocatorDefault, self.dict);
kern_return_t result = IOServiceGetMatchingServices(kIOMasterPortDefault, dict, &serviceIterator);
if (result == KERN_SUCCESS && serviceIterator != IO_OBJECT_NULL) {
while ((service = IOIteratorNext(serviceIterator)) != IO_OBJECT_NULL) {
SInt32 score = 0;
kern_return_t result = IOCreatePlugInInterfaceForService(service,
kIOFireWireAVCLibUnitTypeID,
kIOCFPlugInInterfaceID,
&interface,
&score);
if (result != KERN_SUCCESS) continue;
// result 0xe00002be = kIOReturnNoResources in IOReturn.h
break;
}
}
Additional details
I have find IOCFPlugIn.c in opensource.apple.com. After basic verification,
- IOCreatePlugInInterfaceForService() failed to IOCFPlugIn->Start() .
(*iunknown)->QueryInterface(iunknown, CFUUIDGetUUIDBytes(interfaceType),
(LPVOID *)&interface);
<snip>
kr = (*interface)->Probe(interface, plist, service, &score);
<snip>
haveOne = (kIOReturnSuccess == (*interface)->Start(interface, plist, service));
Probe() returned kIOReturnSuccess though,
Start() failed w/ kIOReturnNoDevice = 0xe00002c0. and haveOne = false.
Finally IOCreatePlugInInterfaceForService() returned kIOReturnNoResources = 0xe00002be.
Is this related to some security feature on macOS?
MODIFIED
I have found hardened runtime with Camera access was rejected FireWireAVCLibUnit (tccd shows error).
Even if no sandbox, no hardened for the XPC Service in Xcode was checked, XPCservice is handled via sandbox. (macOS 10.14.6 + Xcode 10.3)
I would appreciate if you have an advice.
I have found the solution.
- Add NSCameraUsageDescription in Info.plist, and IOFireWireAVCUserClient will work.
- If sandboxed, com.apple.security.device.firewire is also required.
Even if capabilities-sandbox is off, tccd verify info.plist.
If “Privacy - Camera Usage Description” is not available, sandboxd reject to use IOFireWireAVCUserClient device.
Information Property List Key Reference/Cocoa Keys
Related
EDIT:
I have heavily edited this question after making some significant new discoveries and the question not having any answers yet.
Historically/AFAIK, keeping your Mac awake while in closed-display mode and not meeting Apple's requirements, has only been possible with a kernel extension (kext), or a command run as root. Recently however, I have discovered that there must be another way. I could really use some help figuring out how to get this working for use in a (100% free, no IAP) sandboxed Mac App Store (MAS) compatible app.
I have confirmed that some other MAS apps are able to do this, and it looks like they might be writing YES to a key named clamshellSleepDisabled. Or perhaps there's some other trickery involved that causes the key value to be set to YES? I found the function in IOPMrootDomain.cpp:
void IOPMrootDomain::setDisableClamShellSleep( bool val )
{
if (gIOPMWorkLoop->inGate() == false) {
gIOPMWorkLoop->runAction(
OSMemberFunctionCast(IOWorkLoop::Action, this, &IOPMrootDomain::setDisableClamShellSleep),
(OSObject *)this,
(void *)val);
return;
}
else {
DLOG("setDisableClamShellSleep(%x)\n", (uint32_t) val);
if ( clamshellSleepDisabled != val )
{
clamshellSleepDisabled = val;
// If clamshellSleepDisabled is reset to 0, reevaluate if
// system need to go to sleep due to clamshell state
if ( !clamshellSleepDisabled && clamshellClosed)
handlePowerNotification(kLocalEvalClamshellCommand);
}
}
}
I'd like to give this a try and see if that's all it takes, but I don't really have any idea about how to go about calling this function. It's certainly not a part of the IOPMrootDomain documentation, and I can't seem to find any helpful example code for functions that are in the IOPMrootDomain documentation, such as setAggressiveness or setPMAssertionLevel. Here's some evidence of what's going on behind the scenes according to Console:
I've had a tiny bit of experience working with IOMProotDomain via adapting some of ControlPlane's source for another project, but I'm at a loss for how to get started on this. Any help would be greatly appreciated. Thank you!
EDIT:
With #pmdj's contribution/answer, this has been solved!
Full example project:
https://github.com/x74353/CDMManager
This ended up being surprisingly simple/straightforward:
1. Import header:
#import <IOKit/pwr_mgt/IOPMLib.h>
2. Add this function in your implementation file:
IOReturn RootDomain_SetDisableClamShellSleep (io_connect_t root_domain_connection, bool disable)
{
uint32_t num_outputs = 0;
uint32_t input_count = 1;
uint64_t input[input_count];
input[0] = (uint64_t) { disable ? 1 : 0 };
return IOConnectCallScalarMethod(root_domain_connection, kPMSetClamshellSleepState, input, input_count, NULL, &num_outputs);
}
3. Use the following to call the above function from somewhere else in your implementation:
io_connect_t connection = IO_OBJECT_NULL;
io_service_t pmRootDomain = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPMrootDomain"));
IOServiceOpen (pmRootDomain, current_task(), 0, &connection);
// 'enable' is a bool you should assign a YES or NO value to prior to making this call
RootDomain_SetDisableClamShellSleep(connection, enable);
IOServiceClose(connection);
I have no personal experience with the PM root domain, but I do have extensive experience with IOKit, so here goes:
You want IOPMrootDomain::setDisableClamShellSleep() to be called.
A code search for sites calling setDisableClamShellSleep() quickly reveals a location in RootDomainUserClient::externalMethod(), in the file iokit/Kernel/RootDomainUserClient.cpp. This is certainly promising, as externalMethod() is what gets called in response to user space programs calling the IOConnectCall*() family of functions.
Let's dig in:
IOReturn RootDomainUserClient::externalMethod(
uint32_t selector,
IOExternalMethodArguments * arguments,
IOExternalMethodDispatch * dispatch __unused,
OSObject * target __unused,
void * reference __unused )
{
IOReturn ret = kIOReturnBadArgument;
switch (selector)
{
…
…
…
case kPMSetClamshellSleepState:
fOwner->setDisableClamShellSleep(arguments->scalarInput[0] ? true : false);
ret = kIOReturnSuccess;
break;
…
So, to invoke setDisableClamShellSleep() you'll need to:
Open a user client connection to IOPMrootDomain. This looks straightforward, because:
Upon inspection, IOPMrootDomain has an IOUserClientClass property of RootDomainUserClient, so IOServiceOpen() from user space will by default create an RootDomainUserClient instance.
IOPMrootDomain does not override the newUserClient member function, so there are no access controls there.
RootDomainUserClient::initWithTask() does not appear to place any restrictions (e.g. root user, code signing) on the connecting user space process.
So it should simply be a case of running this code in your program:
io_connect_t connection = IO_OBJECT_NULL;
IOReturn ret = IOServiceOpen(
root_domain_service,
current_task(),
0, // user client type, ignored
&connection);
Call the appropriate external method.
From the code excerpt earlier on, we know that the selector must be kPMSetClamshellSleepState.
arguments->scalarInput[0] being zero will call setDisableClamShellSleep(false), while a nonzero value will call setDisableClamShellSleep(true).
This amounts to:
IOReturn RootDomain_SetDisableClamShellSleep(io_connect_t root_domain_connection, bool disable)
{
uint32_t num_outputs = 0;
uint64_t inputs[] = { disable ? 1 : 0 };
return IOConnectCallScalarMethod(
root_domain_connection, kPMSetClamshellSleepState,
&inputs, 1, // 1 = length of array 'inputs'
NULL, &num_outputs);
}
When you're done with your io_connect_t handle, don't forget to IOServiceClose() it.
This should let you toggle clamshell sleep on or off. Note that there does not appear to be any provision for automatically resetting the value to its original state, so if your program crashes or exits without cleaning up after itself, whatever state was last set will remain. This might not be great from a user experience perspective, so perhaps try to defend against it somehow, for example in a crash handler.
as shown in title, i search on google for this question, but there seems that no way get serial number via WPD(Windows Portable Device) api, and in MSDN, i found the WPD_DEVICE_SERIAL_NUMBER property of Portable Device, can anyone tell me how to get this property using wpd api?
The C++ sample can be found here and here
Bit of a process. Basic steps are as follows:
Get and populate a IPortableDeviceValues of your client info
// Create our client information collection
ThrowIfFailed(CoCreateInstance(
CLSID_PortableDeviceValues,
nullptr,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&clientInfo)));
// We have to provide at the least our name, version, revision
ThrowIfFailed(clientInfo->SetStringValue(
WPD_CLIENT_NAME,
L"My super cool WPD client"));
ThrowIfFailed(clientInfo->SetUnsignedIntegerValue(
WPD_CLIENT_MAJOR_VERSION,
1));
ThrowIfFailed(clientInfo->SetUnsignedIntegerValue(
WPD_CLIENT_MINOR_VERSION,
0));
ThrowIfFailed(clientInfo->SetUnsignedIntegerValue(
WPD_CLIENT_REVISION,
1));
Get an IPortableDevice with CoCreateInstance
// A WPD device is represented by an IPortableDevice instance
ThrowIfFailed(CoCreateInstance(
CLSID_PortableDevice,
nullptr,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&device)));
Connect to the device using IPortableDevice::Open, passing the device's ID and the above client info
device->Open(deviceId.c_str(), clientInfo);
Get the device's IPortableDeviceContent using IPortableDevice::Content
CComPtr<IPortableDeviceContent> retVal;
ThrowIfFailedWithMessage(
device.Content(&retVal),
L"! Failed to get IPortableDeviceContent from IPortableDevice");
Get the content's IPortableDeviceProperties using IPortableDeviceContent::Properties
CComPtr<IPortableDeviceProperties> retVal;
ThrowIfFailedWithMessage(
content.Properties(&retVal),
L"! Failed to get IPortableDeviceProperties from IPortableDeviceContent");
Get the properties' IPortableDeviceValues using IPortableDeviceProperties::GetValues, passing "DEVICE" for pszObjectID and nullptr for pKeys
CComPtr<IPortableDeviceValues> retVal;
ThrowIfFailedWithMessage(
properties.GetValues(objectId.c_str(), nullptr, &retVal),
L"! Failed to get IPortableDeviceValues from IPortableDeviceProperties");
Get the serial number from the values using IPortableDeviceValues::GetStringValue, passing WPD_DEVICE_SERIAL_NUMBER for key
propertyKey = WPD_DEVICE_SERIAL_NUMBER;
LPWSTR value = nullptr;
ThrowIfFailedWithMessage(
values.GetStringValue(propertyKey, &value),
L"! Failed to get string value from IPortableDeviceValues");
propertyValue = value;
if (value != nullptr)
{
CoTaskMemFree(value);
}
By no means a complete listing, sorry. The ThrowIf* functions are just basic helpers I wrote to go from checking HRESULTs to throwing exceptions. Hopefully this points you in the right direction.
Additional references:
The dimeby8 blog
WPD Application Programming Interface
I am writing a PCI driver with a character device for an interface (Linux 4.9.13). Here's the scenario that bothers me:
Run touch /dev/foo0 which creates a normal file in the /dev directory.
Load the driver module. Here's a pseudo code representing what happens there (pretty standard character device registration):
// When the module is initialized:
alloc_chrdev_region(&dev, 0, 256, "foo");
class = class_create(THIS_MODULE, "foo");
// Later, when a suitable PCI device is connected the probe function
// calls the following functions:
cdev_init(dev->md_cdev, &fops);
dev->md_devnum = MKDEV(major, 0 + index);
res = cdev_add(dev->md_cdev, dev->md_devnum, 1);
dev->md_sysfsdev = device_create(class, 0, dev->md_devnum, 0, "foo%d", index);
Details:
index is just another free index
What seems weird to me is nothing raises an error that there is already a /dev/foo0 file which is not a character device. I do check all the errors (I think so) but I omitted related code for the sake of conciseness. Everything works as expected if I do not run touch /dev/foo0. Otherwise, I can neither read nor write to the device.
Why is it so? Shouldn't device_create return an error or at least create /dev/foo1 instead?
I have minimum exposure to xcode and I/Okit framework. I have seen device descriptor and configuration descriptor of a usb device in USB prober.
I have written an xcode program using I/O kit framework which gives the usb device name as output, when we give product id and vendor id of that device as input.
/*Take the vendor and product id from console*/
printf("\nEnter the vendor id : ");
scanf("%lx",&usbVendor);
printf("\nEnter the product id :");
scanf("%lx",&usbProduct);
/* Set up a matching dictionary for the class */
matchingDict = IOServiceMatching(kIOUSBDeviceClassName);
if (matchingDict == NULL)
{
return -1; // fail
}
// Create a CFNumber for the idVendor and set the value in the dictionary
numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &usbVendor);
CFDictionarySetValue(matchingDict,
CFSTR(kUSBVendorID),
numberRef);
CFRelease(numberRef);
// Create a CFNumber for the idProduct and set the value in the dictionary
numberRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &usbProduct);
CFDictionarySetValue(matchingDict,
CFSTR(kUSBProductID),
numberRef);
CFRelease(numberRef);
numberRef = NULL;
/*Get an iterator.*/
kr = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &iter);
if (kr != KERN_SUCCESS)
{
return -1;// fail
}
/* iterate */
while ((device = IOIteratorNext(iter)))
{
/*Display the device names */
io_name_t deviceName;
kr = IORegistryEntryGetName(device, deviceName);
if (KERN_SUCCESS != kr) {
deviceName[0] = '\0';
}
printf("\ndeviceName:%s",deviceName);
/*Free the reference taken before continuing to the next item */
IOObjectRelease(device);
}
/*Release the iterator */
IOObjectRelease(iter);
return 0;
}
I need to modify this, so that on giving vendor and product id of usb device, i will get the device descriptor and configuration descriptor( as shown in USB prober) as output.
Here i just gave an example, code can change but the output must be the descriptor( atleast the device decriptor).
Thanks in advance...
I think u should download the source code of USBProber rather than figure it out by yourself.
All the information presents in the USBProber u could get sooner or later by analyzing the source code.
Here is link to download the source code of IOUSBFamily, with USBProber inside it.
http://opensource.apple.com/tarballs/IOUSBFamily/
To get the configuration descriptors you can use code like this:
IOUSBDeviceInterface650** dev = ...;
IOUSBConfigurationDescriptor* configDesc = nullptr;
// Get the configuration descriptor for the first configuration (configuration 0).
kern_return_t kr = (*dev)->GetConfigurationDescriptorPtr(dev, 0, &configDesc);
if (kr != kIOReturnSuccess)
return an_error;
// Now use configDesc->...
Unfortunately there doesn't seem to be a function to get the device descriptor. There are functions to get some of it:
kr = (*dev)->GetDeviceClass(dev, &desc.bDeviceClass);
kr = (*dev)->GetDeviceSubClass(dev, &desc.bDeviceSubClass);
kr = (*dev)->GetDeviceProtocol(dev, &desc.bDeviceProtocol);
kr = (*dev)->GetDeviceVendor(dev, &desc.idVendor);
kr = (*dev)->GetDeviceProduct(dev, &desc.idProduct);
kr = (*dev)->GetDeviceReleaseNumber(dev, &desc.bcdDevice);
kr = (*dev)->GetNumberOfConfigurations(dev, &desc.bNumConfigurations);
But I don't see a way to get iManufacturer, iProduct, iSerial, bMaxPacketSize0, or bcdUSB.
There is a way around this - instead of using the built-in functions you can just do a control request to get the device descriptor (and configuration descriptors if you like) manually using a control transfer.
The USB 2.0 spec describes how to do this. Basically you:
Do a control transfer with bmRequestType = Device | Standard | In, bRequest = USB_GET_DESCRIPTOR_REQUEST, wValue = (USB_DEVICE_DESCRIPTOR_TYPE << 8), wIndex = 0, wLength = 2. That will fail because the descriptor is longer than 2, but it gets you the descriptor header which includes its length.
Repeat that request but with the correct length.
For configuration descriptors, do a third request with length wTotalLength.
You can do it with one less request since you know the size of the descriptors in advance, but I like to do it like that because then you can wrap it up in a very general getDescriptor() method.
In theory you can do it as simply as this:
IOUSBDeviceDescriptor devDesc;
IOUSBDevRequest request;
request.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice);
request.bRequest = kUSBRqGetDescriptor;
request.wValue = kUSBDeviceDesc << 8;
request.wIndex = 0;
request.wLength = sizeof(devDesc); // 18
request.pData = &devDesc;
request.wLenDone = 0;
kern_return_t kr = (*dev)->DeviceRequest(dev, &request);
But for some reason that is giving me a kIOUSBPipeStalled error. Not sure why.
Edit: I forgot the << 8. Now it works. :-)
The header IOKit/usb/USBSpec.h has a documented list of property keys corresponding to values inside the different descriptors. You can use those with IORegistryEntrySearchCFProperty (or similar functions) to get the descriptor values. This way you don't need a device request from an IOUSBDeviceInterface, which is advantageous because:
the documentation (comments) say that all device requests require an opened USB device and you may not have permission to do that for all devices (it's possible the documentation is wrong, at least for descriptor requests, but I have no guarantee of that and it seems better to follow it anyway)
device requests can block for an indeterminate amount of time
the manufacturer, product, and serial number strings (which are referenced by the device descriptor, but are not technically part of it) are not retrieved in this request
For getting device descriptor and configuration decriptor, we can use functions in IOUSBDeviceInterface class
Link: http://developer.apple.com/library/mac/#documentation/Darwin/Reference/IOKit/IOUSBLib_h/Classes/IOUSBDeviceInterface/index.html#//apple_ref/doc/com/intfm/IOUSBDeviceInterface/
For getting interface descriptor and end point descriptor, we can use functions in IOUSBInterfaceInterface class
Link: http://developer.apple.com/library/mac/#documentation/Darwin/Reference/IOKit/IOUSBLib_h/Classes/IOUSBInterfaceInterface/
I recall there being a Cocoa framework or AppleScript dictionary to check if an Application bundle with a specific name is installed at all, anywhere on the computer.
How do I do this? Either Cocoa, AppleScript, or command line are useful to me.
You should use Launch Services to do this, specifically the function LSFindApplicationForInfo().
You use it like so:
#import <ApplicationServices/ApplicationServices.h>
CFURLRef appURL = NULL;
OSStatus result = LSFindApplicationForInfo (
kLSUnknownCreator, //creator codes are dead, so we don't care about it
CFSTR("com.apple.Safari"), //you can use the bundle ID here
NULL, //or the name of the app here (CFSTR("Safari.app"))
NULL, //this is used if you want an FSRef rather than a CFURLRef
&appURL
);
switch(result)
{
case noErr:
NSLog(#"the app's URL is: %#",appURL);
break;
case kLSApplicationNotFoundErr:
NSLog(#"app not found");
break;
default:
NSLog(#"an error occurred: %d",result);
break;
}
//the CFURLRef returned from the function is retained as per the docs so we must release it
if(appURL)
CFRelease(appURL);
From the command line this seems to do it:
> mdfind 'kMDItemContentType == "com.apple.application-bundle" && kMDItemFSName = "Google Chrome.app"'
You can also use lsregister.
on doesAppExist(appName)
if (do shell script "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister -dump | grep com.apple.Safari") ¬
contains "com.apple.Safari" then return true
end appExists
That's pretty fast and you can do it from other languages like Python quite easily. You would want to play around with what you grep to make it most efficient.