Bug 1888033 - [Menu Redesign] Add a secret setting and feature flag for the menu...
[gecko.git] / toolkit / xre / MacLaunchHelper.mm
blob127e7081e1fa3fea2cc60de85cfe3775da665c97
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"
11 #include <Cocoa/Cocoa.h>
12 #include <crt_externs.h>
13 #include <ServiceManagement/ServiceManagement.h>
14 #include <Security/Authorization.h>
15 #include <spawn.h>
16 #include <stdio.h>
18 using namespace mozilla;
20 void LaunchChildMac(int aArgc, char** aArgv, pid_t* aPid) {
21   MacAutoreleasePool pool;
23   @try {
24     NSString* launchPath = [NSString stringWithUTF8String:aArgv[0]];
25     NSMutableArray* arguments = [NSMutableArray arrayWithCapacity:aArgc - 1];
26     for (int i = 1; i < aArgc; i++) {
27       [arguments addObject:[NSString stringWithUTF8String:aArgv[i]]];
28     }
29     NSTask* child = [NSTask launchedTaskWithLaunchPath:launchPath
30                                              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,
48       &authRef);
49   if (status != errAuthorizationSuccess) {
50     // AuthorizationCreate really shouldn't fail.
51     NSLog(@"AuthorizationCreate failed! NSOSStatusErrorDomain / %d",
52           (int)status);
53     return NO;
54   }
56   BOOL result = NO;
57   AuthorizationItem authItem = {kSMRightBlessPrivilegedHelper, 0, NULL, 0};
58   AuthorizationRights authRights = {1, &authItem};
59   AuthorizationFlags flags =
60       kAuthorizationFlagDefaults | kAuthorizationFlagInteractionAllowed |
61       kAuthorizationFlagPreAuthorize | kAuthorizationFlagExtendRights;
63   // Obtain the right to install our privileged helper tool.
64   status = AuthorizationCopyRights(authRef, &authRights,
65                                    kAuthorizationEmptyEnvironment, flags, NULL);
66   if (status != errAuthorizationSuccess) {
67     NSLog(@"AuthorizationCopyRights failed! NSOSStatusErrorDomain / %d",
68           (int)status);
69   } else {
70     CFErrorRef cfError;
71     // This does all the work of verifying the helper tool against the
72     // application and vice-versa. Once verification has passed, the embedded
73     // launchd.plist is extracted and placed in /Library/LaunchDaemons and then
74     // loaded. The executable is placed in /Library/PrivilegedHelperTools.
75     result = (BOOL)SMJobBless(kSMDomainSystemLaunchd,
76                               (CFStringRef) @"org.mozilla.updater", authRef,
77                               &cfError);
78     if (!result) {
79       NSLog(@"Unable to install helper!");
80       CFRelease(cfError);
81     }
82   }
84   return result;
87 void AbortElevatedUpdate() {
88   mozilla::MacAutoreleasePool pool;
90   id updateServer = nil;
91   int currTry = 0;
92   const int numRetries = 10;  // Number of IPC connection retries before
93                               // giving up.
94   while (currTry < numRetries) {
95     @try {
96       updateServer = (id)[NSConnection
97           rootProxyForConnectionWithRegisteredName:@"org.mozilla.updater.server"
98                                               host:nil
99                                    usingNameServer:[NSSocketPortNameServer
100                                                        sharedInstance]];
101       if (updateServer && [updateServer respondsToSelector:@selector(abort)]) {
102         [updateServer performSelector:@selector(abort)];
103         return;
104       }
105       NSLog(@"Server doesn't exist or doesn't provide correct selectors.");
106       sleep(1);  // Wait 1 second.
107       currTry++;
108     } @catch (NSException* e) {
109       NSLog(@"Encountered exception, retrying: %@: %@", e.name, e.reason);
110       sleep(1);  // Wait 1 second.
111       currTry++;
112     }
113   }
114   NSLog(@"Unable to clean up updater.");
117 bool LaunchElevatedUpdate(int aArgc, char** aArgv, pid_t* aPid) {
118   LaunchChildMac(aArgc, aArgv, aPid);
119   bool didSucceed = InstallPrivilegedHelper();
120   if (!didSucceed) {
121     AbortElevatedUpdate();
122   }
123   return didSucceed;