SMJobBless and friends - objective-c

i made an App that was submitted to the Mac AppStore. For some reasons, they refused it, because it was installing a helper tool using the SMJobBless API.
As this helper tool isn't necessary for most of the App's functionality, i have removed it, and my application got accepted.
So right now, i am packaging a standalone installer for the helper tool that would be downloadable on the internet.
However, after i have installed the files in place, the helper tool refuses to run... The helper tool just has a plist that goes into /Library/LaunchDaemons and a binary that goes into /Library/PrivilegedHelperTools.
Now i'm wondering, what exactly is SMJobBless doing more than moving files into place? Is it registering my tool with launchctl? i tried various things, to manually add it to launchctl, without success: the helper tool just exits after requesting it's checkin request.
The helper tool just contains that:
#autoreleasepool {
launch_data_t req = launch_data_new_string(LAUNCH_KEY_CHECKIN);
launch_data_t resp = launch_msg(req); // AT THIS POINT, RESP IS NULL. Why?
launch_data_t machData = launch_data_dict_lookup(resp, LAUNCH_JOBKEY_MACHSERVICES);
launch_data_t machPData = launch_data_dict_lookup(machData, [kHelperBundleName UTF8String]);
mach_port_t mp = launch_data_get_machport(machPData);
launch_data_free (resp);
launch_data_free (req);
// Prepare connexion.
NSMachPort *rp = [[NSMachPort alloc] initWithMachPort:mp];
PrivilegedHelperProxy
*phProxy = [[[PrivilegedHelperProxy alloc] init] autorelease];
NSConnection *con = [NSConnection connectionWithReceivePort:rp sendPort:nil];
[rp release];
[con setRootObject:phProxy];
[[NSRunLoop currentRunLoop] run];
}
return EXIT_SUCCESS;
This service was running flawlessly when getting installed via the SMJobBless API, and the root proxy was accessible from within my APP...
So what would be the required steps for my installer to get this helper tool properly working, as it would with calls to the SMJobBless API?
Thanks,
Pierre.

Solved. The PList entry for the helper tool wasn't including any ProgramArguments (This didn't seem to be an issue with the SMJobBless API). After adding those to the PList, the helper tool can correctly run, and the installer registers it correctly.
Thanks anyways!
Pierre.

Related

OTPublisherSettings *settings = [[OTPublisherSettings alloc] init]; getting Bad receiver type 'int32_t' (aka 'int')

While compiling
OTPublisherSettings *settings = [[OTPublisherSettings alloc] init];
getting compiler error.
Bad receiver type 'int32_t' (aka 'int')
Using the latest Xcode, happening on all unmodified sample apps, looked in lib headers dir under Pods, Found the OTPublisher.h and OTPublisherKit.h headers, cannot seem to find the OTPublisherSettings class. Am I missing something?
I just did a fresh install of sample apps with pod install without any errors. Did you see any issues when you were trying to install the SDK using cocoapods. What SDK version are you using ?

How to launch app's FinderSync Extension from code?

I've made finder sync extension using instruction.
It displays in finder when I launch it directly from Xcode. But it is not visible when I run the main app. Is there some way to do it for sandbox app? In not sandboxed app I used the code:
NSTask *fseEnable = [[NSTask alloc] init];
fseEnable.launchPath = #"/usr/bin/pluginkit";
fseEnable.arguments = #[#"-e", #"use", #"-i", #"com.team.AppName.FinderSyncExtension"];
[fseEnable launch];
But it does not work for sandboxed. I tried:
[[NSWorkspace sharedWorkspace] launchAppWithBundleIdentifier:#"com.team.AppName.FinderSyncExtension" options:NSWorkspaceLaunchDefault additionalEventParamDescriptor:nil launchIdentifier:nil];
But It also did not work.
Probably there is some checkbox in project settings to do it. but I haven't fond it.
Also I've found probably sad news in the documentation.
After installing an app extension, a user must take action to enable
it. Often, users can enable an extension within the context of their
current task. If your extension is a Today widget, for example, users
can edit the Today view in Notification Center to enable your
extension. In other cases, users can use Settings (in iOS) or System
Preferences (in macOS) to enable and manage the extensions they
install.
Use int system(const char *) instead of NSTask.
system("pluginkit -e use -i com.team.AppName.FinderSyncExtension");

How can I programmatically determine if my OSX app was launched from the Applications dir?

I've built an app for OSX and built a .dmg installer for it. However, some users have trouble following the HUGE arrow in the background image telling them to drag the app to the /Applications folder. ;-)
I would be nice if it's possible to do the following:
somehow detect if the app was launched from the /Applications dir
if it wasn't, offer to automatically move the app from the dmg to the /Applications dir, for the user, and launch from there
if #2 isn't something that the OS would allow, display an alert and then close down the app.
I can figure out #3 on my own, but I'm wondering if #1 and #2 are possible and how I might go about them.
[NSBundle mainBundle] represents your application bundle. You can use this to get the app's URL.
You can then use NSURL to get the volume information for the app's URL and determine if it was launched from within the DMG. E.g.:
NSURL* bundleUrl = [[NSBundle mainBundle] bundleURL];
NSString* volumeName = nil;
if ([bundleUrl getResourceValue:&volumeName forKey:NSURLVolumeNameKey error:nil])
{
if ([volumeName isEqual:#"My App Installer"]) // or whatever your DMG volume name is
{
[self tryMigratingToAppsFolder];
}
}
Or, you could just check if it's not in /Applications. Keep in mind that some people install apps in different locations.
A full list of NSURL's available volume resources is available here.
Start here https://github.com/potionfactory/LetsMove/ and check out some of the forks if it isn't quite what you want.
Pretty sure there is another library that does this as well but I can't find it right now
For Unix in general, the first argument to the application (argv[0]), is a pointer to the absolute path to the application binary. Check the path for "/Applications". Also, if the app has been launched from the mounted dmg, the path will begin with "/Volumes" if it has been mounted by double-clicking it from the Finder.
You can see the difference using ps:
Launched from the mounted dmg (using Identity Finder.app as an example):
$ ps aux|grep Identity
michael 52525 0.0 0.5 926072 80804 ?? U 12:14PM 0:02.92 /Volumes/Identity Finder/Identity Finder.app/Contents/MacOS/Identity Finder
Launched from /Applications:
$ ps aux|grep Identity
michael 52562 0.0 0.7 976212 116736 ?? S 12:16PM 0:03.24 /Applications/Identity Finder.app/Contents/MacOS/Identity Finder

Sandboxing, login items and launch App

Trying to make my app works with sandboxing I get stuck in this problem:
I need to add my App to login items so I've followed these steps:
Creating an HelperApp.app (with is agent = YES) that at the open exec this code :
NSString *appPath = #"/Applications/MyNewApp.app";
[[NSWorkspace sharedWorkspace] launchApplication:appPath];
[NSApp terminate:nil];
This app is sandboxed.
I added a copy build phase to my MyNewApp.app bundle with subpath Contents/Library/LoginItems and destination wrapper
And obviously also the MyNewApp.app is sandboxed
In MyNewApp.app I registered with SMLoginItemSetEnabled the bundle id for the HelperApp.app
Now all works correctly but... when MyNewApp.app tries to launch MyNewApp.app I get an error:
LSOpenFromURLSpec() returned -10827 for application /Applications/MyNewApp.app path (null).
But if I try to use the HelperApp.app to launch a not sandboxed (i.e. Steam.app) app it works like a charm!
Is there another way to launch the app? probably NSWorkSpace works with functions not allowed in sandbox context. What do you suggest?
You may want to have a look at my tutorial on how to get launch at login working in the sandbox.

Cannot access keychain item after SMJobBless update

We have a problem with updating a helper tool with SMJobBless that has been puzzling us for days now.
We are developing an application for which at some point we need to perform administrative tasks (loading/unloading a kext). We are also using the keychain to store account information for our application.
For the administrative tasks, we use a helper tool that is installed using SMJobBless with which we communicate using DO over Mach ports (with NSConnection).
In the helper tool:
// use our bundle id as our service name
NSString* name = [[NSBundle mainBundle] bundleIdentifier];
launch_data_t checkinRequest = launch_data_new_string(LAUNCH_KEY_CHECKIN);
launch_data_t checkinResponse = launch_msg(checkinRequest);
launch_data_t machServicesDict = launch_data_dict_lookup(checkinResponse, LAUNCH_JOBKEY_MACHSERVICES);
launch_data_t machPort = launch_data_dict_lookup(machServicesDict, [name UTF8String]);
mach_port_t mp = launch_data_get_machport(machPort);
launch_data_free(checkinResponse);
launch_data_free(checkinRequest);
NSMachPort *receivePort = [[NSMachPort alloc] initWithMachPort:mp];
NSConnection *server = [NSConnection connectionWithReceivePort:receivePort sendPort:nil];
In the app:
NSConnection *conn = [NSConnection connectionWithRegisteredName:HELPER_BUNDLE_IDENTIFIER host:nil];
id proxyServerObject = [conn rootProxy];
if(conn && proxyServerObject) {
return [proxyServerObject someMethod];
}
return NO;
We sign both the application and the helper tool using a codesign certificate from Thawte. So far, everything works like a charm. The helper tool is installed and we can communicate with it using DO; our kext is loaded and unloaded successfully.
The problem starts when we try to update our helper tool. We use the info dictionary of the installed tool and the bundled tool in our app bundle to check whether an update of the tool is required and call SMJobBless again to perform the update.
After the SMJobBless call, the following lines appear in the Console:
6/19/12 10:31:24.000 AM kernel: CODE SIGNING: cs_invalid_page(0x104e17000): p=74362[OURAPP] clearing CS_VALID
6/19/12 10:31:24.000 AM kernel: CODE SIGNING: cs_invalid_page(0x10d0de000): p=74364[OURAPPHELPER] clearing CS_VALID
After this, the application is unable to read the application password from our keychain item, the function SecKeychainItemCopyContent returns errSecAuthFailed (-25293). However, no error is reported if we manually verify the code signature of our installed helper tool or application bundle using codesign -vvvv PATH_TO_TOOL_OR_BUNDLE.
The tool and application are signed outside of the Xcode environment and the contents are not altered after the signing process.
We have found one other post that describes a similar situation, but that question is still unanswered.
A related issue might be SMJobBless returning error 4098.
We are testing on OSX 10.7.4.
Anyone faced similar issues or is there something obvious that we are doing wrong?
This is due to a bug related to how SMJobBless replaces the helper tool on disk. In particular, it modifies the binary in place rather than taking the common approach of writing to a temporary file and then renaming it over top of the destination. The effect of this is that if the binary is in memory, the modifications to the file change the memory pages backing the file, invalidating their code signature. I've written up a bug report about this as rdar://problem/13514523. I'd encourage you to file your own if you've not done so already.
A possible workaround may be to have your application ask the helper tool to remove itself from disk before you use SMJobBless to upgrade it. This should result in SMJobBless copying to a new file on disk, bypassing the issue.