1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "remoting/host/installer/mac/uninstaller/remoting_uninstaller.h"
7 #import <Cocoa/Cocoa.h>
9 #include "base/mac/scoped_authorizationref.h"
10 #include "remoting/host/constants_mac.h"
13 void logOutput(FILE* pipe) {
16 long bytesRead = read(fileno(pipe), readBuffer, sizeof(readBuffer) - 1);
19 readBuffer[bytesRead] = '\0';
20 NSLog(@"%s", readBuffer);
24 NSArray* convertToNSArray(const char** array) {
25 NSMutableArray* ns_array = [[[NSMutableArray alloc] init] autorelease];
27 const char* element = array[i++];
28 while (element != nullptr) {
29 [ns_array addObject:[NSString stringWithUTF8String:element]];
35 @implementation RemotingUninstaller
38 const char kKeystoneAdmin[] = "/Library/Google/GoogleSoftwareUpdate/"
39 "GoogleSoftwareUpdate.bundle/Contents/MacOS/"
41 const char kKeystonePID[] = "com.google.chrome_remote_desktop";
43 - (void)runCommand:(const char*)cmd
44 withArguments:(const char**)args {
46 NSPipe* output = [NSPipe pipe];
49 NSArray* arg_array = convertToNSArray(args);
50 NSLog(@"Executing: %s %@", cmd, [arg_array componentsJoinedByString:@" "]);
53 task = [[[NSTask alloc] init] autorelease];
54 [task setLaunchPath:[NSString stringWithUTF8String:cmd]];
55 [task setArguments:arg_array];
56 [task setStandardInput:[NSPipe pipe]];
57 [task setStandardOutput:output];
60 NSData* data = [[output fileHandleForReading] readDataToEndOfFile];
64 if ([task terminationStatus] != 0) {
65 // TODO(garykac): When we switch to sdk_10.6, show the
66 // [task terminationReason] as well.
67 NSLog(@"Command terminated status=%d", [task terminationStatus]);
70 result = [[[NSString alloc] initWithData:data
71 encoding:NSUTF8StringEncoding]
73 if ([result length] != 0) {
74 NSLog(@"Result: %@", result);
77 @catch (NSException* exception) {
78 NSLog(@"Exception %@ %@", [exception name], [exception reason]);
82 - (void)sudoCommand:(const char*)cmd
83 withArguments:(const char**)args
84 usingAuth:(AuthorizationRef)authRef {
85 NSArray* arg_array = convertToNSArray(args);
86 NSLog(@"Executing (as Admin): %s %@", cmd,
87 [arg_array componentsJoinedByString:@" "]);
90 status = AuthorizationExecuteWithPrivileges(authRef, cmd,
91 kAuthorizationFlagDefaults,
95 if (status == errAuthorizationToolExecuteFailure) {
96 NSLog(@"Error errAuthorizationToolExecuteFailure");
97 } else if (status != errAuthorizationSuccess) {
98 NSLog(@"Error while executing %s. Status=%d",
99 cmd, static_cast<int>(status));
108 - (void)sudoDelete:(const char*)filename
109 usingAuth:(AuthorizationRef)authRef {
110 const char* args[] = { "-rf", filename, nullptr };
111 [self sudoCommand:"/bin/rm" withArguments:args usingAuth:authRef];
114 - (void)shutdownService {
115 const char* launchCtl = "/bin/launchctl";
116 const char* argsStop[] = { "stop", remoting::kServiceName, nullptr };
117 [self runCommand:launchCtl withArguments:argsStop];
119 if ([[NSFileManager defaultManager] fileExistsAtPath:
120 [NSString stringWithUTF8String:remoting::kServicePlistPath]]) {
121 const char* argsUnload[] = { "unload", "-w", "-S", "Aqua",
122 remoting::kServicePlistPath, nullptr };
123 [self runCommand:launchCtl withArguments:argsUnload];
127 - (void)keystoneUnregisterUsingAuth:(AuthorizationRef)authRef {
128 const char* args[] = {"--delete", "--productid", kKeystonePID, "-S", nullptr};
129 [self sudoCommand:kKeystoneAdmin withArguments:args usingAuth:authRef];
132 - (void)remotingUninstallUsingAuth:(AuthorizationRef)authRef {
133 // Remove the enabled file before shutting down the service or else it might
135 [self sudoDelete:remoting::kHostEnabledPath usingAuth:authRef];
137 [self shutdownService];
139 [self sudoDelete:remoting::kServicePlistPath usingAuth:authRef];
140 [self sudoDelete:remoting::kHostBinaryPath usingAuth:authRef];
141 [self sudoDelete:remoting::kHostHelperScriptPath usingAuth:authRef];
142 [self sudoDelete:remoting::kHostConfigFilePath usingAuth:authRef];
143 [self sudoDelete:remoting::kPrefPaneFilePath usingAuth:authRef];
144 [self sudoDelete:remoting::kLogFilePath usingAuth:authRef];
145 [self sudoDelete:remoting::kLogFileConfigPath usingAuth:authRef];
146 [self sudoDelete:remoting::kNativeMessagingManifestPath usingAuth:authRef];
147 [self sudoDelete:remoting::kBrandedUninstallerPath usingAuth:authRef];
148 [self sudoDelete:remoting::kUnbrandedUninstallerPath usingAuth:authRef];
150 [self keystoneUnregisterUsingAuth:authRef];
153 - (OSStatus)remotingUninstall {
154 base::mac::ScopedAuthorizationRef authRef;
156 AuthorizationCreate(nullptr, kAuthorizationEmptyEnvironment,
157 kAuthorizationFlagDefaults, authRef.get_pointer());
158 if (status != errAuthorizationSuccess) {
159 [NSException raise:@"AuthorizationCreate Failure"
160 format:@"Error during AuthorizationCreate status=%d",
161 static_cast<int>(status)];
164 AuthorizationItem right = {kAuthorizationRightExecute, 0, nullptr, 0};
165 AuthorizationRights rights = {1, &right};
166 AuthorizationFlags flags = kAuthorizationFlagDefaults |
167 kAuthorizationFlagInteractionAllowed |
168 kAuthorizationFlagPreAuthorize |
169 kAuthorizationFlagExtendRights;
170 status = AuthorizationCopyRights(authRef, &rights, nullptr, flags, nullptr);
171 if (status == errAuthorizationSuccess) {
172 RemotingUninstaller* uninstaller =
173 [[[RemotingUninstaller alloc] init] autorelease];
174 [uninstaller remotingUninstallUsingAuth:authRef];