Re: Bridging Installer Plug in and PostInstall - Summary
Re: Bridging Installer Plug in and PostInstall - Summary
- Subject: Re: Bridging Installer Plug in and PostInstall - Summary
- From: Colin Ryan <email@hidden>
- Date: Thu, 18 Oct 2018 14:00:00 -0400
Ok folks, it's not 100% what I was hoping to accomplish but I've managed
to achieve my Installer Plug-In goals...so thought I'd share the results
with everyone and maybe help someone else out in the future. I had never
done Objective-C before, nor written a plugin in OSX etc so took more
time that it should of and seasoned pro's likely could have done this in
a day...so trying to help any future newbies. Obviously it was all
learning so evening binding variables from the .nib to the plug-in etc
were all new to me, but the items below were the biggest challenges.
* Requirement:
Needed another Installer screen (via Plug-in) that requested a 3
parameters, these were then sent to Web Services Requests for
verification. The verification's were also a series of HTTPS calls that
needed to be in order. Upon being verified these same parameters were
to be used to get some additional information. The big issue here is
that the 3 parameters could be considered somewhat sensitive, so I
didn't want them sticking around on a filesystem even in temp space. So
I had my Plug-In call it's own script that utilizes the sensitive bits
and that script put the results - which were not so sensitive - into a
fixed temp spaces where the postinstall script could use them. Not
perfect but as far as I could tell with the Plug-In essentially acting
like it's own sand-boxed process there was a walled garden around it
that I couldn't cross to/from the parent Installer.app process.
Complete code is not included just what for me where the key obstacle
type bits....
* Solution consisted of:
Encapsulating a series of NSUrlSessionTasks that cascaded within each
others completionHandlers blocks to provider the order of operations.
This was because the verification could be done over a series of
possible configurations so needed to try 1 if that failed then try a
second etc etc.
NSURLSessionDataTask *postDataTask = [commonSession dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (!data)
{
// No Connection here try an alternate
NSURLSessionDataTask *postDataTask444 = [ commonSession
dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse
*response, NSError *error) {
if(!data)
{
// No Connection on 2nd try so try another alternate
NSURLSessionDataTask *postDataTask9292 = [ commonSession
dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse
*response, NSError *error) {
if(!data)
{
// Failed again so we'll give up.
} else {
// Finally got a response on 3rd so deal with
results.
}
}];
[postDataTask9292 resume];
} else {
// Got the response on the 2nd Try so deal with results.
}
}];
[postDataTask444 resume]
}
else
{
// Got a Reponse on the first try so deal with results.
}
}];
[postDataTask resume];
* Now of course these are async requests to to make sure they actually
happened in order I had to wrap the whole method in a semaphore
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
[self _doURLMethodAbove:session::^(BOOL success){
leave = success;
dispatch_semaphore_signal(sema);
}];
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
* Now having verified the 3 parameters I made a bunch of other calls in
similiar fashion to the above. Once that was done I needed to call a
script with these parameters as arguments. But how to reference the
script? where is the script? etc. Turns out I put the script as a
resource in the Plug-In and then could refer to it consistently by way of:
NSBundle * myself = [NSBundle bundleForClass:[self class]];
NSString * postpath = [myself pathForResource:@"myscript" ofType:@""]; //gives
me a handle on the script firmly rooted in the context of the plug-in (i.e. I knew where it was)
* Now Execute the script...I didn't need elevated permissions to run
this but it can be done with the right syntax to the AppleScript object.
This code was courtesy of Carlos P via Michael from
stackoverflow....this is a bit of a Hack but
|AuthorizationExecuteWithPrivileges()| has been deprecated and frankly
for what I needed and my Obj-C level getting in the new Job/Service
management frameworks just to run a script now()...well...call me lazy.
- (BOOL) runProcessAsAdministrator:(NSString*)scriptPath
withArguments:(NSArray *)arguments
output:(NSString **)output
errorDescription:(NSString **)errorDescription {
NSString * allArgs = [arguments componentsJoinedByString:@" "];
NSString * fullScript = [NSString stringWithFormat:@"'%@' %@", scriptPath,
allArgs];
NSDictionary *errorInfo = [NSDictionary new];
NSString *script = [NSString stringWithFormat:@"do shell script \"%@\" with
administrator privileges", fullScript];
NSAppleScript *appleScript = [[NSAppleScript new] initWithSource:script];
NSAppleEventDescriptor * eventResult = [appleScript
executeAndReturnError:&errorInfo];
// Check errorInfo
if (! eventResult)
{
// Describe common errors
*errorDescription = nil;
if ([errorInfo valueForKey:NSAppleScriptErrorNumber])
{
NSNumber * errorNumber = (NSNumber *)[errorInfo
valueForKey:NSAppleScriptErrorNumber];
if ([errorNumber intValue] == -128)
*errorDescription = @"The administrator password is required to do
this.";
}
// Set error message from provided message
if (*errorDescription == nil)
{
if ([errorInfo valueForKey:NSAppleScriptErrorMessage])
*errorDescription = (NSString *)[errorInfo
valueForKey:NSAppleScriptErrorMessage];
}
return NO;
}
else
{
// Set output to the AppleScript's output
*output = [eventResult stringValue];
return YES;
}
}
Usage example:
NSString * output = nil;
NSString * processErrorDescription = nil;
BOOL success = [self runProcessAsAdministrator:@"/usr/bin/id"
withArguments:[NSArray arrayWithObjects:@"-un", nil]
output:&output
errorDescription:&processErrorDescription];
if (!success) // Process failed to run
{
// ...look at errorDescription
}
else
{
// ...process output
}
And that was it. This script call above moved stuff into a /private/tmp
directory and my postinstaller script picked them up from there then
deleted them.
If there was some way to share variables or even just the sandbox of the
parent Installer.app that would be great, but seems there isn't...it may
be just me but on one hand the use of a full blown plug-in this context
is great because you have the entire toolkit of Obj-C do achieve what
you want, but being so disconnected from the overall Installer process
seems a major miss.
Anyhow hope this helps someone in the future.
Colin
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Installer-dev mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden