How can an OS X application get itself restarted when it crashes - macos

There are quite a few hints about how to keep programs alive, and restart them when they crash:
By using a shell script
By using launchd
They all have one restriction in common, though: They all need to be run before the to-be-monitored program.
In my case, though, I have a faceless background app that has to decide on its own whether it wants to be restarted in case it crashes. So, my app would be launched first and cannot be quit to become a child of a monitoring process (for explanation see below).
I've tried the launchd solution by writing a plist file like this:
<?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.mydomain.myapp.restartAfterCrash</string>
<key>ProgramArguments</key>
<array>
<string>/Applications/MyApp.app/Contents/MacOS/MyApp</string>
</array>
</dict>
</plist>
It nearly does what I need, but in order to activate it from my app, by invoking it with launchctl load /path/to/file.plist, it wants to launch my app once more, so that my originally running app would have to quit to let the new instance run.
However, I cannot pass control to another instance of my app, because it's causing several new problems if the app was launched by double clicking a document the Finder or if it was launched by an AppleScript that wants to communicate further with my app. In those cases, my originally launched app must keep running, and thus the launchd method is not viable, nor is any other method that requires my app to run a new instance instead.
In short, are there other tested (reliable) methods that a Mac application can use to have itself restarted only if it crashes?
The only method I can think of is launching another faceless background application that monitors the first program's lifetime, e.g. via its PID.
I also wonder if it can be done with a simple shell command invocation that would periodically check whether my app is still alive, as those always run as a child process and would die along with the primary app, won't they?

Related

How to match my dext with an USB device in DriverKit?

I am writing a driverkit extension whose goal is to block USB devices, such as flash drives. As a starting point, I chose example project at https://developer.apple.com/documentation/driverkit/communicating_between_a_driverkit_extension_and_a_client_app?language=objc
In effort not to disable a keyboard or a mouse, firstly I am trying to match my dext with a single concrete USB drive, whose vendorId I found in the registry (being aware of hexa to decimal conversions). The problem is that when the flash drive is plugged in, the system doesn't match my dext and continues with the system one.
What's wrong? Is a provisioning profile required for this? Is it even possible to match any device this way?
Driver's entitlement file:
<?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>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.developer.driverkit</key>
<true/>
<key>com.apple.developer.driverkit.transport.usb</key>
<array>
<dict>
<key>idVendor</key>
<integer>9128</integer>
</dict>
</array>
</dict>
</plist>
Info.plist:
<?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>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>IOKitPersonalities</key>
<dict>
<key>DeviceControlDriver</key>
<dict>
<key>idVendor</key>
<integer>9128</integer>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleIdentifierKernel</key>
<string>com.apple.kpi.iokit</string>
<key>IOClass</key>
<string>IOUserService</string>
<key>IOProviderClass</key>
<string>IOUSBHostDevice</string>
<key>IOUserClass</key>
<string>DeviceControlDriver</string>
<key>IOUserServerName</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>UserClientProperties</key>
<dict>
<key>IOClass</key>
<string>IOUserUserClient</string>
<key>IOUserClass</key>
<string>DeviceControlDriver</string>
</dict>
</dict>
</dict>
<key>OSBundleUsageDescription</key>
<string></string>
</dict>
</plist>
There are a few sub-questions here, and I'll try to address them individually:
In effort not to disable a keyboard or a mouse, firstly I am trying to match my dext with a single concrete USB drive, whose vendorId I found in the registry (being aware of hexa to decimal conversions). The problem is that when the flash drive is plugged in, the system doesn't match my dext and continues with the system one.
[…]
<key>idVendor</key>
<integer>9128</integer>
…
<key>IOProviderClass</key>
<string>IOUSBHostDevice</string>
Matching USB devices on macOS follows very specific rules. If you do not follow one of the matching patterns outlined in this documentation from Apple, matching will generally fail.
You're attempting to match on idVendor alone; this will not work, you will need to match on either idVendor + idProduct or one of the other patterns listed.
What's wrong? Is a provisioning profile required for this?
Is it even possible to match any device this way?
If you wish to distribute your driver, or even run it locally with SIP enabled, you will require a provisioning profile. Note that Apple generally issues entitlements for specific requested vendor IDs only; if your needs go beyond that you'll need to get in touch with Apple directly.
For local testing without valid code signing, you can disable SIP; you will still require valid embedded entitlements in the signature, but you can use a provisioning profile which doesn't match the entitlements.
I am writing a driverkit extension whose goal is to block USB devices, such as flash drives.
Note that this will be of somewhat limited effectiveness, as 3rd party kexts and DriverKit extensions are not considered for matching during early system boot. This means that any devices which can be claimed by Apple's drivers and which are already connected at the time macOS starts up will always match Apple's own drivers, not yours.
For USB, you can to some extent work around this by forcing re-enumeration using the USBDeviceReEnumerate function. Note that this simulates a hard unplug, so for mounted storage devices, you should unmount cleanly first, for example.

Chrome and Preview image viewing displays garbage

I know this is not a directly programming related question, but after skimming in the list of SE.com sites, I think this is my best shot of getting a decent answer.
Every once in a while, when view high definition images, they appear like uninitialised memory on the GPU. Like so:
While the actual image is should look like this (taken from Google image search results):
This happens in Chrome and Preview on OS X on my laptop. Now, I'm wondering what this is caused by: broken hardware? A bug in the hardware? Or just a bug in software (like the driver for the GPU or OS X itself)? I think that a bug in both Chrome and Preview is unlikely. Am I the only one experiencing this every once in a while? I believe that Chrome uses OpenGL.
For people blaming the Beta version of OS X: I had this before the beta as well.
My specs:
Intel HD Graphics 3000
MacBookPro8,1
OS X 10.11.2 Beta (15C47a)
And the version.plist file in the AppleIntelHD3000Graphics.kext kernel extension:
<?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>BuildVersion</key>
<string>419</string>
<key>CFBundleShortVersionString</key>
<string>10.0.40</string>
<key>CFBundleVersion</key>
<string>10.0.0</string>
<key>ProjectName</key>
<string>GraphicsDrivers</string>
<key>SourceVersion</key>
<string>10000040000000</string>
</dict>
</plist>

Prevent classic drivers from opening usb device on OS X

I'm working on kext driver for my USB device on OS X. In this driver I have a pointer to object IOUSBDevice *device (it can be received in start() and probe() functions) and I have a problem: it is possible to open device (device->open(this, kIOServiceSeize)) in probe() function, but in other functions open()return false, because it seems like classic driver take the control on device.
I found article User-Mode USB Device Arbitration and tried to create "skeleton" kext to set "ClassicMustNotSeize" property to true, but it seems like it is not working, I still can't open the device.
<?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>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>com.sample.iokit.ClassicNotSeizeDriver</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>KEXT</string>
<key>CFBundleShortVersionString</key>
<string>1.0.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0.0</string>
<key>IOKitPersonalities</key>
<dict>
<key>MyUSBDevice</key>
<dict>
<key>CFBundleIdentifier</key>
<string>com.apple.driver.AppleUSBMergeNub</string>
<key>IOClass</key>
<string>AppleUSBMergeNub</string>
<key>IOProviderClass</key>
<string>IOUSBDevice</string>
<key>IOProviderMergeProperties</key>
<dict>
<key>ClassicMustNotSeize</key>
<true/>
</dict>
<key>idProduct</key>
<integer>1</integer>
<key>idVendor</key>
<integer>10978</integer>
</dict>
</dict>
<key>OSBundleLibraries</key>
<dict>
<key>com.apple.driver.AppleUSBMergeNub</key>
<string>1.8.3b1</string>
<key>com.apple.iokit.IOUSBFamily</key>
<string>1.8</string>
</dict>
</dict>
</plist>
Is it possible to set "ClassicMustNotSeize" property programmatically, for example in probe() function of my driver?
I tried:
device->setProperty("ClassicMustNotSeize", true);
and it seems like it also doesn't work.
Other kexts will not claim a service if yours has successfully matched and probe()d. If you don't successfully start(), it might get matched by another kext later if something re-initiates matching. So something isn't right, and it's not possible to tell from your question alone what it is because you haven't provided the relevant code, info.plist and ioreg excerpt, let alone your code's debug output…
So I can only guess:
Why do you think you need to use kIOServiceSeize? If your kext matches the device, has the highest probe score and returns non-null from probe(), no other driver will have grabbed the device (yet). If you want to ensure exclusive access to USB devices, use the kUSBOptionBitOpenExclusivelyMask option to open().
If you open() during probe(), make sure you close() before returning, no matter what value you return. Re-open() in start(). (Overriding probe() is usually not needed anyway, it will return this by default.)
Are you setting a match category in your I/O kit personality? Unless you have a very good reason, don't.
Are you matching an IOUSBInterface instead of the IOUSBDevice in your actual driver personality, and then traversing the service graph? Or somehow otherwise trying to claim the device other than by I/O kit matching?
Is your kext in /Library/Extensions (or /System/Library/Extensions if there's a good reason it can't be in /LE)? If not, it won't participate in matching.
I very much doubt this has anything to do with "Classic" which as far as I'm aware was a way of running Mac OS 8 & 9 applications in early versions of OS X. So unless you're targeting 10.1 or so, ClassicMustNotSeize does nothing.

XPC communication between service and client app works only when launched from xcode

XPC communication works fine when I launch the targets from my Xcode. But, when I manually launch the service and client apps by double clicking them on the icons, the connection invalidation message shows up.Whay do they work fine inside xcode and not from outside ?.What magic is Xcode doing here to make the communication work.
For all folks, who met the same problem using xpc_connection_create_mach_service: XPC works through a lot of launchd stuff; when we are debugging the app in xCode, it mediates the app and launchd connection, but without xCode we need to do it ourselves.
To get this alive, the one should start it through launchd using the launch plist file.
There is an example of such plist here, but it's not enough.
The trick is the MachServices key, which looks like:
<key>MachServices</key>
<dict>
<key>com.server.bundle-id</key>
<true/>
</dict>
This is the way we are creating the mach service, so I guess our XPC connection may have some mach port below it.

Send keystrokes to frontmost app from sandboxed cocoa app

I need to send keystrokes to front most app from my cooca app.
I already have working code for it by using CGEventCreateKeyboardEvent() and AXUIElementPostKeyboardEvent(), but it only works if app is not sandboxed.
I have searched google for the same, but didn't find any working solution.
I saw that a Text app and few others doing the same thing in sandboxed environment, so i am wondering, if someone help me to figure out, that how aText.app and others are able to send keystrokes in sandbox environment.
Thanks,
This is actually possible. I have made an example app available here - SendKey at GitHub
I took the easy road and started with a simple AppleScript:
delay 5
tell application "System Events"
repeat 10 times
keystroke "#"
end repeat
end tell
The 'delay' in the script simply gives me enough time to make a text editor the frontmost application. I would suggest starting with just running this script to see what it does.
Then, I created an Xcode project using the default Application template and wrote:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
NSString* scriptPath = [[NSBundle mainBundle] pathForResource:#"sendkey" ofType:#"scpt"];
NSURL* scriptURL = [NSURL fileURLWithPath:scriptPath];
NSDictionary* errors;
NSAppleScript* script = [[NSAppleScript alloc] initWithContentsOfURL:scriptURL error:&errors];
NSLog( #"%#", errors );
[script executeAndReturnError:&errors];
NSLog( #"%#", errors );
}
I tested this without turning on sandboxing to verified it works and it did. Then I turned on Sandboxing and, of course, it broke. But, fortunately, there is a way around that. For now, Apple is providing a temporary entitlement called com.apple.security.temporary-exception.apple-events. And, you can request the exception be granted for 'com.apple.systemevents'. This is what my entitlements file looks like:
<?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>com.apple.security.temporary-exception.apple-events</key>
<array>
<string>com.apple.systemevents</string>
</array>
<key>com.apple.security.app-sandbox</key>
<true />
</dict>
</plist>
Once I added this entitlement to my sandboxed app and signed it, it worked as expected again.
Now, if you want to send other keys, this question & answer will demonstrate how to build your script on the fly - Passing variables to an applescript.
Of course, once you have all of these working, you can probably turn to NSAppleEventDescriptor and related classes to build the event in code, but I haven't played with that technique.
Please note that Apple does suggest you do the following when using a temporary entitlement:
If you choose not to sandbox your app now or to use a temporary
exception entitlement, use Apple’s bug reporting system to let Apple
know about the issue you are encountering. Apple considers feature
requests as it develops the OS X platform. Also, be sure use the
Review Notes field in iTunes Connect to explain why the exception is
needed.

Resources