OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "remoting/host/installer/mac/uninstaller/remoting_uninstaller.h" | 5 #include "remoting/host/installer/mac/uninstaller/remoting_uninstaller.h" |
6 | 6 |
7 #import <Cocoa/Cocoa.h> | 7 #import <Cocoa/Cocoa.h> |
8 | 8 |
9 #include "base/mac/scoped_authorizationref.h" | 9 #include "base/mac/scoped_authorizationref.h" |
10 #include "base/mac/scoped_cftyperef.h" | |
11 #include "remoting/host/constants_mac.h" | 10 #include "remoting/host/constants_mac.h" |
12 | 11 |
13 @implementation RemotingUninstallerAppDelegate | 12 @implementation RemotingUninstaller |
14 | |
15 NSString* const kLaunchAgentsDir = @"/Library/LaunchAgents"; | |
16 NSString* const kPrefPaneDir = @"/Library/PreferencePanes"; | |
17 NSString* const kHelperToolsDir = @"/Library/PrivilegedHelperTools"; | |
18 NSString* const kApplicationDir = @"/Applications"; | |
19 | |
20 NSString* const kPrefPaneName = @kServiceName ".prefPane"; | |
21 NSString* const kUninstallerName = | |
22 @"Chrome Remote Desktop Host Uninstaller.app"; | |
23 | 13 |
24 // Keystone | 14 // Keystone |
25 const char kKeystoneAdmin[] = "/Library/Google/GoogleSoftwareUpdate/" | 15 const char kKeystoneAdmin[] = "/Library/Google/GoogleSoftwareUpdate/" |
26 "GoogleSoftwareUpdate.bundle/Contents/MacOS/" | 16 "GoogleSoftwareUpdate.bundle/Contents/MacOS/" |
27 "ksadmin"; | 17 "ksadmin"; |
28 const char kKeystonePID[] = "com.google.chrome_remote_desktop"; | 18 const char kKeystonePID[] = "com.google.chrome_remote_desktop"; |
29 | 19 |
30 - (void)dealloc { | |
31 [super dealloc]; | |
32 } | |
33 | |
34 - (void)applicationDidFinishLaunching:(NSNotification*)aNotification { | |
35 } | |
36 | |
37 - (void)logOutput:(FILE*) pipe { | 20 - (void)logOutput:(FILE*) pipe { |
38 char readBuffer[128]; | 21 char readBuffer[128]; |
39 for (;;) { | 22 for (;;) { |
40 long bytesRead = read(fileno(pipe), readBuffer, sizeof(readBuffer) - 1); | 23 long bytesRead = read(fileno(pipe), readBuffer, sizeof(readBuffer) - 1); |
41 if (bytesRead < 1) | 24 if (bytesRead < 1) |
42 break; | 25 break; |
43 readBuffer[bytesRead] = '\0'; | 26 readBuffer[bytesRead] = '\0'; |
44 NSLog(@"%s", readBuffer); | 27 NSLog(@"%s", readBuffer); |
45 } | 28 } |
46 } | 29 } |
47 | 30 |
48 - (void)messageBox:(const char*)message { | 31 - (NSArray*)convertToNSArray:(const char**)array { |
Lambros
2012/07/23 22:06:47
Is it normal ObjC++ style to add these kinds of pr
garykac
2012/07/23 22:41:09
Not sure about style. I moved it out as a plain fu
| |
49 base::mac::ScopedCFTypeRef<CFStringRef> message_ref( | 32 NSMutableArray* ns_array = [[[NSMutableArray alloc] init] autorelease]; |
50 CFStringCreateWithCString(NULL, message, (int)strlen(message))); | 33 int i = 0; |
51 CFOptionFlags result; | 34 const char* element = array[i++]; |
52 CFUserNotificationDisplayAlert(0, kCFUserNotificationNoteAlertLevel, | 35 while (element != NULL) { |
53 NULL, NULL, NULL, | 36 [ns_array addObject:[NSString stringWithUTF8String:element]]; |
54 CFSTR("Chrome Remote Desktop Uninstaller"), | 37 element = array[i++]; |
55 message_ref, NULL, NULL, NULL, &result); | 38 } |
39 return ns_array; | |
56 } | 40 } |
57 | 41 |
58 -(void)runCommand:(NSString*)cmd | 42 - (void)runCommand:(const char*)cmd |
59 withArguments:(NSArray*)args { | 43 withArguments:(const char**)args { |
60 NSTask* task; | 44 NSTask* task; |
61 NSPipe* output = [NSPipe pipe]; | 45 NSPipe* output = [NSPipe pipe]; |
62 NSString* result; | 46 NSString* result; |
63 | 47 |
64 NSLog(@"Executing: %@ %@", cmd, [args componentsJoinedByString:@" "]); | 48 NSArray* arg_array = [self convertToNSArray:args]; |
49 NSLog(@"Executing: %s %@", cmd, [arg_array componentsJoinedByString:@" "]); | |
65 | 50 |
66 @try { | 51 @try { |
67 task = [[[NSTask alloc] init] autorelease]; | 52 task = [[[NSTask alloc] init] autorelease]; |
68 [task setLaunchPath:cmd]; | 53 [task setLaunchPath:[NSString stringWithUTF8String:cmd]]; |
69 [task setArguments:args]; | 54 [task setArguments:arg_array]; |
70 [task setStandardInput:[NSPipe pipe]]; | 55 [task setStandardInput:[NSPipe pipe]]; |
71 [task setStandardOutput:output]; | 56 [task setStandardOutput:output]; |
72 [task launch]; | 57 [task launch]; |
73 | 58 |
74 NSData* data = [[output fileHandleForReading] readDataToEndOfFile]; | 59 NSData* data = [[output fileHandleForReading] readDataToEndOfFile]; |
75 | 60 |
76 [task waitUntilExit]; | 61 [task waitUntilExit]; |
77 | 62 |
78 if ([task terminationStatus] != 0) { | 63 if ([task terminationStatus] != 0) { |
79 // TODO(garykac): When we switch to sdk_10.6, show the | 64 // TODO(garykac): When we switch to sdk_10.6, show the |
(...skipping 10 matching lines...) Expand all Loading... | |
90 } | 75 } |
91 @catch (NSException* exception) { | 76 @catch (NSException* exception) { |
92 NSLog(@"Exception %@ %@", [exception name], [exception reason]); | 77 NSLog(@"Exception %@ %@", [exception name], [exception reason]); |
93 } | 78 } |
94 } | 79 } |
95 | 80 |
96 - (void)sudoCommand:(const char*)cmd | 81 - (void)sudoCommand:(const char*)cmd |
97 withArguments:(const char**)args | 82 withArguments:(const char**)args |
98 usingAuth:(AuthorizationRef)authRef { | 83 usingAuth:(AuthorizationRef)authRef { |
99 | 84 |
100 NSMutableArray* arg_array = [[[NSMutableArray alloc] init] autorelease]; | 85 if (authRef == nil) { |
101 int i = 0; | 86 // We're running as root, so just run the command directly. |
Lambros
2012/07/23 20:42:35
This feels wrong to me - are you absolutely sure y
garykac
2012/07/23 22:41:09
Hmm.. Previous tests made me think this was requir
| |
102 const char* arg = args[i++]; | 87 [self runCommand:cmd withArguments:args]; |
103 while (arg != NULL) { | 88 return; |
104 [arg_array addObject:[NSString stringWithUTF8String:arg]]; | |
105 arg = args[i++]; | |
106 } | 89 } |
90 | |
91 NSArray* arg_array = [self convertToNSArray:args]; | |
107 NSLog(@"Executing (as Admin): %s %@", cmd, | 92 NSLog(@"Executing (as Admin): %s %@", cmd, |
108 [arg_array componentsJoinedByString:@" "]); | 93 [arg_array componentsJoinedByString:@" "]); |
109 FILE* pipe = NULL; | 94 FILE* pipe = NULL; |
110 OSStatus status; | 95 OSStatus status; |
111 status = AuthorizationExecuteWithPrivileges(authRef, cmd, | 96 status = AuthorizationExecuteWithPrivileges(authRef, cmd, |
112 kAuthorizationFlagDefaults, | 97 kAuthorizationFlagDefaults, |
113 (char* const*)args, | 98 (char* const*)args, |
114 &pipe); | 99 &pipe); |
115 | 100 |
116 if (status == errAuthorizationToolExecuteFailure) { | 101 if (status == errAuthorizationToolExecuteFailure) { |
117 NSLog(@"Error errAuthorizationToolExecuteFailure"); | 102 NSLog(@"Error errAuthorizationToolExecuteFailure"); |
118 } else if (status != errAuthorizationSuccess) { | 103 } else if (status != errAuthorizationSuccess) { |
119 NSLog(@"Error while executing %s. Status=%lx", cmd, status); | 104 NSLog(@"Error while executing %s. Status=%lx", cmd, status); |
120 } else { | 105 } else { |
121 [self logOutput:pipe]; | 106 [self logOutput:pipe]; |
122 } | 107 } |
123 | 108 |
124 if (pipe != NULL) | 109 if (pipe != NULL) |
125 fclose(pipe); | 110 fclose(pipe); |
126 } | 111 } |
127 | 112 |
128 - (void)sudoDelete:(const char*)filename | 113 - (void)sudoDelete:(const char*)filename |
129 usingAuth:(AuthorizationRef)authRef { | 114 usingAuth:(AuthorizationRef)authRef { |
130 const char* args[] = { "-rf", filename, NULL }; | 115 const char* args[] = { "-rf", filename, NULL }; |
131 [self sudoCommand:"/bin/rm" withArguments:args usingAuth:authRef]; | 116 [self sudoCommand:"/bin/rm" withArguments:args usingAuth:authRef]; |
132 } | 117 } |
133 | 118 |
134 -(void)shutdownService { | 119 - (void)shutdownService { |
135 NSString* launchCtl = @"/bin/launchctl"; | 120 const char* launchCtl = "/bin/launchctl"; |
136 NSArray* argsStop = [NSArray arrayWithObjects:@"stop", | 121 const char* argsStop[] = { "stop", remoting::kServiceName, NULL }; |
137 @kServiceName, nil]; | |
138 [self runCommand:launchCtl withArguments:argsStop]; | 122 [self runCommand:launchCtl withArguments:argsStop]; |
139 | 123 |
140 NSString* plist = [NSString stringWithFormat:@"%@/%@.plist", | 124 if ([[NSFileManager defaultManager] fileExistsAtPath: |
141 kLaunchAgentsDir, @kServiceName]; | 125 [NSString stringWithUTF8String:remoting::kServicePlistPath]]) { |
142 if ([[NSFileManager defaultManager] fileExistsAtPath:plist]) { | 126 const char* argsUnload[] = { "unload", "-w", "-S", "Aqua", |
143 NSArray* argsUnload = [NSArray arrayWithObjects:@"unload", | 127 remoting::kServicePlistPath, NULL }; |
144 @"-w", @"-S", @"Aqua", plist, nil]; | |
145 [self runCommand:launchCtl withArguments:argsUnload]; | 128 [self runCommand:launchCtl withArguments:argsUnload]; |
146 } | 129 } |
147 } | 130 } |
148 | 131 |
149 -(void)keystoneUnregisterUsingAuth:(AuthorizationRef)authRef { | 132 - (void)keystoneUnregisterUsingAuth:(AuthorizationRef)authRef { |
150 const char* args[] = { "--delete", "--productid", kKeystonePID, "-S", NULL }; | 133 const char* args[] = { "--delete", "--productid", kKeystonePID, "-S", NULL }; |
151 [self sudoCommand:kKeystoneAdmin withArguments:args usingAuth:authRef]; | 134 [self sudoCommand:kKeystoneAdmin withArguments:args usingAuth:authRef]; |
152 } | 135 } |
153 | 136 |
154 -(void)remotingUninstallUsingAuth:(AuthorizationRef)authRef { | 137 - (void)remotingUninstallUsingAuth:(AuthorizationRef)authRef { |
155 NSString* host_enabled = [NSString stringWithFormat:@"%@/%@.me2me_enabled", | 138 // Remove the enabled file before shutting down the service or else it might |
156 kHelperToolsDir, @kServiceName]; | 139 // restart itself. |
157 [self sudoDelete:[host_enabled UTF8String] usingAuth:authRef]; | 140 [self sudoDelete:remoting::kHostEnabledPath usingAuth:authRef]; |
158 | 141 |
159 [self shutdownService]; | 142 [self shutdownService]; |
160 | 143 |
161 NSString* plist = [NSString stringWithFormat:@"%@/%@.plist", | 144 [self sudoDelete:remoting::kServicePlistPath usingAuth:authRef]; |
162 kLaunchAgentsDir, @kServiceName]; | 145 [self sudoDelete:remoting::kHostBinaryPath usingAuth:authRef]; |
163 [self sudoDelete:[plist UTF8String] usingAuth:authRef]; | 146 [self sudoDelete:remoting::kHostHelperScriptPath usingAuth:authRef]; |
164 | 147 [self sudoDelete:remoting::kHostConfigFilePath usingAuth:authRef]; |
165 NSString* host_binary = [NSString stringWithFormat:@"%@/%@.me2me_host.app", | 148 [self sudoDelete:remoting::kPrefPaneFilePath usingAuth:authRef]; |
166 kHelperToolsDir, @kServiceName]; | 149 [self sudoDelete:remoting::kBrandedUninstallerPath usingAuth:authRef]; |
167 [self sudoDelete:[host_binary UTF8String] usingAuth:authRef]; | 150 [self sudoDelete:remoting::kUnbrandedUninstallerPath usingAuth:authRef]; |
168 | |
169 NSString* host_script = [NSString stringWithFormat:@"%@/%@.me2me.sh", | |
170 kHelperToolsDir, @kServiceName]; | |
171 [self sudoDelete:[host_script UTF8String] usingAuth:authRef]; | |
172 | |
173 NSString* auth = [NSString stringWithFormat:@"%@/%@.json", | |
174 kHelperToolsDir, @kServiceName]; | |
175 [self sudoDelete:[auth UTF8String] usingAuth:authRef]; | |
176 | |
177 NSString* prefpane = [NSString stringWithFormat:@"%@/%@", | |
178 kPrefPaneDir, kPrefPaneName]; | |
179 [self sudoDelete:[prefpane UTF8String] usingAuth:authRef]; | |
180 | |
181 NSString* uninstaller = [NSString stringWithFormat:@"%@/%@", | |
182 kApplicationDir, kUninstallerName]; | |
183 [self sudoDelete:[uninstaller UTF8String] usingAuth:authRef]; | |
184 | 151 |
185 [self keystoneUnregisterUsingAuth:authRef]; | 152 [self keystoneUnregisterUsingAuth:authRef]; |
186 } | 153 } |
187 | 154 |
188 - (IBAction)uninstall:(NSButton*)sender { | |
189 base::mac::ScopedAuthorizationRef authRef; | |
190 | |
191 NSLog(@"Chrome Remote Desktop uninstall starting."); | |
192 | |
193 @try { | |
194 OSStatus status; | |
195 status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, | |
196 kAuthorizationFlagDefaults, &authRef); | |
197 if (status != errAuthorizationSuccess) { | |
198 [NSException raise:@"AuthorizationCreate Failure" | |
199 format:@"Error during AuthorizationCreate status=%ld", status]; | |
200 } | |
201 | |
202 AuthorizationItem right = {kAuthorizationRightExecute, 0, NULL, 0}; | |
203 AuthorizationRights rights = {1, &right}; | |
204 AuthorizationFlags flags = kAuthorizationFlagDefaults | | |
205 kAuthorizationFlagInteractionAllowed | | |
206 kAuthorizationFlagPreAuthorize | | |
207 kAuthorizationFlagExtendRights; | |
208 status = AuthorizationCopyRights(authRef, &rights, NULL, flags, NULL); | |
209 if (status == errAuthorizationCanceled) { | |
210 NSLog(@"Chrome Remote Desktop Host uninstall canceled."); | |
211 const char* message = "Chrome Remote Desktop Host uninstall canceled."; | |
212 [self messageBox:message]; | |
213 } else if (status == errAuthorizationSuccess) { | |
214 [self remotingUninstallUsingAuth:authRef]; | |
215 | |
216 NSLog(@"Chrome Remote Desktop Host uninstall complete."); | |
217 const char* message = | |
218 "Chrome Remote Desktop Host was successfully uninstalled."; | |
219 [self messageBox:message]; | |
220 } else { | |
221 [NSException raise:@"AuthorizationCopyRights Failure" | |
222 format:@"Error during AuthorizationCopyRights status=%ld", status]; | |
223 } | |
224 } | |
225 @catch (NSException* exception) { | |
226 NSLog(@"Exception %@ %@", [exception name], [exception reason]); | |
227 const char* message = | |
228 "Error! Unable to uninstall Chrome Remote Desktop Host."; | |
229 [self messageBox:message]; | |
230 } | |
231 | |
232 [NSApp terminate:self]; | |
233 } | |
234 | |
235 - (IBAction)cancel:(id)sender { | |
236 [NSApp terminate:self]; | |
237 } | |
238 | |
239 - (IBAction)handleMenuClose:(NSMenuItem*)sender { | |
240 [NSApp terminate:self]; | |
241 } | |
242 | |
243 @end | 155 @end |
244 | |
245 int main(int argc, char* argv[]) | |
246 { | |
247 return NSApplicationMain(argc, (const char**)argv); | |
248 } | |
249 | |
OLD | NEW |