Bug 1708422: part 13) Factor code out to `mozInlineSpellChecker::SpellCheckerTimeSlic...
[gecko.git] / toolkit / xre / MacLaunchHelper.mm
blobec570ffab124abf78ad95e69c99e46a8304cc145
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "MacLaunchHelper.h"
8 #include "MacAutoreleasePool.h"
9 #include "mozilla/UniquePtr.h"
10 #include "nsMemory.h"
12 #include <Cocoa/Cocoa.h>
13 #include <crt_externs.h>
14 #include <ServiceManagement/ServiceManagement.h>
15 #include <Security/Authorization.h>
16 #include <spawn.h>
17 #include <stdio.h>
19 using namespace mozilla;
21 void LaunchChildMac(int aArgc, char** aArgv, pid_t* aPid) {
22   MacAutoreleasePool pool;
24   @try {
25     NSString* launchPath = [NSString stringWithUTF8String:aArgv[0]];
26     NSMutableArray* arguments = [NSMutableArray arrayWithCapacity:aArgc - 1];
27     for (int i = 1; i < aArgc; i++) {
28       [arguments addObject:[NSString stringWithUTF8String:aArgv[i]]];
29     }
30     NSTask* child = [NSTask launchedTaskWithLaunchPath:launchPath arguments:arguments];
31     if (aPid) {
32       *aPid = [child processIdentifier];
33       // We used to use waitpid to wait for the process to terminate. This is
34       // incompatible with NSTask and we wait for the process to exit here
35       // instead.
36       [child waitUntilExit];
37     }
38   } @catch (NSException* e) {
39     NSLog(@"%@: %@", e.name, e.reason);
40   }
43 BOOL InstallPrivilegedHelper() {
44   AuthorizationRef authRef = NULL;
45   OSStatus status = AuthorizationCreate(
46       NULL, kAuthorizationEmptyEnvironment,
47       kAuthorizationFlagDefaults | kAuthorizationFlagInteractionAllowed, &authRef);
48   if (status != errAuthorizationSuccess) {
49     // AuthorizationCreate really shouldn't fail.
50     NSLog(@"AuthorizationCreate failed! NSOSStatusErrorDomain / %d", (int)status);
51     return NO;
52   }
54   BOOL result = NO;
55   AuthorizationItem authItem = {kSMRightBlessPrivilegedHelper, 0, NULL, 0};
56   AuthorizationRights authRights = {1, &authItem};
57   AuthorizationFlags flags = kAuthorizationFlagDefaults | kAuthorizationFlagInteractionAllowed |
58                              kAuthorizationFlagPreAuthorize | kAuthorizationFlagExtendRights;
60   // Obtain the right to install our privileged helper tool.
61   status =
62       AuthorizationCopyRights(authRef, &authRights, kAuthorizationEmptyEnvironment, flags, NULL);
63   if (status != errAuthorizationSuccess) {
64     NSLog(@"AuthorizationCopyRights failed! NSOSStatusErrorDomain / %d", (int)status);
65   } else {
66     CFErrorRef cfError;
67     // This does all the work of verifying the helper tool against the
68     // application and vice-versa. Once verification has passed, the embedded
69     // launchd.plist is extracted and placed in /Library/LaunchDaemons and then
70     // loaded. The executable is placed in /Library/PrivilegedHelperTools.
71     result = (BOOL)SMJobBless(kSMDomainSystemLaunchd, (CFStringRef) @"org.mozilla.updater", authRef,
72                               &cfError);
73     if (!result) {
74       NSLog(@"Unable to install helper!");
75       CFRelease(cfError);
76     }
77   }
79   return result;
82 void AbortElevatedUpdate() {
83   mozilla::MacAutoreleasePool pool;
85   id updateServer = nil;
86   int currTry = 0;
87   const int numRetries = 10;  // Number of IPC connection retries before
88                               // giving up.
89   while (currTry < numRetries) {
90     @try {
91       updateServer = (id)[NSConnection
92           rootProxyForConnectionWithRegisteredName:@"org.mozilla.updater.server"
93                                               host:nil
94                                    usingNameServer:[NSSocketPortNameServer sharedInstance]];
95       if (updateServer && [updateServer respondsToSelector:@selector(abort)]) {
96         [updateServer performSelector:@selector(abort)];
97         return;
98       }
99       NSLog(@"Server doesn't exist or doesn't provide correct selectors.");
100       sleep(1);  // Wait 1 second.
101       currTry++;
102     } @catch (NSException* e) {
103       NSLog(@"Encountered exception, retrying: %@: %@", e.name, e.reason);
104       sleep(1);  // Wait 1 second.
105       currTry++;
106     }
107   }
108   NSLog(@"Unable to clean up updater.");
111 bool LaunchElevatedUpdate(int aArgc, char** aArgv, pid_t* aPid) {
112   LaunchChildMac(aArgc, aArgv, aPid);
113   bool didSucceed = InstallPrivilegedHelper();
114   if (!didSucceed) {
115     AbortElevatedUpdate();
116   }
117   return didSucceed;