Home | History | Annotate | Download | only in uninstaller
      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