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. 4 5 #include "remoting/host/installer/mac/uninstaller/remoting_uninstaller.h" 6 7 #import <Cocoa/Cocoa.h> 8 9 #include "base/mac/scoped_authorizationref.h" 10 #include "remoting/host/constants_mac.h" 11 12 13 void logOutput(FILE* pipe) { 14 char readBuffer[128]; 15 for (;;) { 16 long bytesRead = read(fileno(pipe), readBuffer, sizeof(readBuffer) - 1); 17 if (bytesRead < 1) 18 break; 19 readBuffer[bytesRead] = '\0'; 20 NSLog(@"%s", readBuffer); 21 } 22 } 23 24 NSArray* convertToNSArray(const char** array) { 25 NSMutableArray* ns_array = [[[NSMutableArray alloc] init] autorelease]; 26 int i = 0; 27 const char* element = array[i++]; 28 while (element != NULL) { 29 [ns_array addObject:[NSString stringWithUTF8String:element]]; 30 element = array[i++]; 31 } 32 return ns_array; 33 } 34 35 @implementation RemotingUninstaller 36 37 // Keystone 38 const char kKeystoneAdmin[] = "/Library/Google/GoogleSoftwareUpdate/" 39 "GoogleSoftwareUpdate.bundle/Contents/MacOS/" 40 "ksadmin"; 41 const char kKeystonePID[] = "com.google.chrome_remote_desktop"; 42 43 - (void)runCommand:(const char*)cmd 44 withArguments:(const char**)args { 45 NSTask* task; 46 NSPipe* output = [NSPipe pipe]; 47 NSString* result; 48 49 NSArray* arg_array = convertToNSArray(args); 50 NSLog(@"Executing: %s %@", cmd, [arg_array componentsJoinedByString:@" "]); 51 52 @try { 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]; 58 [task launch]; 59 60 NSData* data = [[output fileHandleForReading] readDataToEndOfFile]; 61 62 [task waitUntilExit]; 63 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]); 68 } 69 70 result = [[[NSString alloc] initWithData:data 71 encoding:NSUTF8StringEncoding] 72 autorelease]; 73 if ([result length] != 0) { 74 NSLog(@"Result: %@", result); 75 } 76 } 77 @catch (NSException* exception) { 78 NSLog(@"Exception %@ %@", [exception name], [exception reason]); 79 } 80 } 81 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:@" "]); 88 FILE* pipe = NULL; 89 OSStatus status; 90 status = AuthorizationExecuteWithPrivileges(authRef, cmd, 91 kAuthorizationFlagDefaults, 92 (char* const*)args, 93 &pipe); 94 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)); 100 } else { 101 logOutput(pipe); 102 } 103 104 if (pipe != NULL) 105 fclose(pipe); 106 } 107 108 - (void)sudoDelete:(const char*)filename 109 usingAuth:(AuthorizationRef)authRef { 110 const char* args[] = { "-rf", filename, NULL }; 111 [self sudoCommand:"/bin/rm" withArguments:args usingAuth:authRef]; 112 } 113 114 - (void)shutdownService { 115 const char* launchCtl = "/bin/launchctl"; 116 const char* argsStop[] = { "stop", remoting::kServiceName, NULL }; 117 [self runCommand:launchCtl withArguments:argsStop]; 118 119 if ([[NSFileManager defaultManager] fileExistsAtPath: 120 [NSString stringWithUTF8String:remoting::kServicePlistPath]]) { 121 const char* argsUnload[] = { "unload", "-w", "-S", "Aqua", 122 remoting::kServicePlistPath, NULL }; 123 [self runCommand:launchCtl withArguments:argsUnload]; 124 } 125 } 126 127 - (void)keystoneUnregisterUsingAuth:(AuthorizationRef)authRef { 128 const char* args[] = { "--delete", "--productid", kKeystonePID, "-S", NULL }; 129 [self sudoCommand:kKeystoneAdmin withArguments:args usingAuth:authRef]; 130 } 131 132 - (void)remotingUninstallUsingAuth:(AuthorizationRef)authRef { 133 // Remove the enabled file before shutting down the service or else it might 134 // restart itself. 135 [self sudoDelete:remoting::kHostEnabledPath usingAuth:authRef]; 136 137 [self shutdownService]; 138 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]; 149 150 [self keystoneUnregisterUsingAuth:authRef]; 151 } 152 153 - (OSStatus)remotingUninstall { 154 base::mac::ScopedAuthorizationRef authRef; 155 OSStatus status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, 156 kAuthorizationFlagDefaults, &authRef); 157 if (status != errAuthorizationSuccess) { 158 [NSException raise:@"AuthorizationCreate Failure" 159 format:@"Error during AuthorizationCreate status=%d", 160 static_cast<int>(status)]; 161 } 162 163 AuthorizationItem right = {kAuthorizationRightExecute, 0, NULL, 0}; 164 AuthorizationRights rights = {1, &right}; 165 AuthorizationFlags flags = kAuthorizationFlagDefaults | 166 kAuthorizationFlagInteractionAllowed | 167 kAuthorizationFlagPreAuthorize | 168 kAuthorizationFlagExtendRights; 169 status = AuthorizationCopyRights(authRef, &rights, NULL, flags, NULL); 170 if (status == errAuthorizationSuccess) { 171 RemotingUninstaller* uninstaller = 172 [[[RemotingUninstaller alloc] init] autorelease]; 173 [uninstaller remotingUninstallUsingAuth:authRef]; 174 } 175 return status; 176 } 177 178 @end 179