Chromium Code Reviews| 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/plugin/daemon_controller.h" | 5 #include "remoting/host/plugin/daemon_controller.h" |
| 6 | 6 |
| 7 #include <launch.h> | 7 #include <launch.h> |
| 8 #include <stdio.h> | 8 #include <stdio.h> |
| 9 #include <sys/types.h> | 9 #include <sys/types.h> |
| 10 | 10 |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 30 namespace { | 30 namespace { |
| 31 | 31 |
| 32 // The NSSystemDirectories.h header has a conflicting definition of | 32 // The NSSystemDirectories.h header has a conflicting definition of |
| 33 // NSSearchPathDirectory with the one in base/mac/foundation_util.h. | 33 // NSSearchPathDirectory with the one in base/mac/foundation_util.h. |
| 34 // Foundation.h would work, but it can only be included from Objective-C files. | 34 // Foundation.h would work, but it can only be included from Objective-C files. |
| 35 // Therefore, we define the needed constants here. | 35 // Therefore, we define the needed constants here. |
| 36 const int NSLibraryDirectory = 5; | 36 const int NSLibraryDirectory = 5; |
| 37 | 37 |
| 38 // The name of the Remoting Host service that is registered with launchd. | 38 // The name of the Remoting Host service that is registered with launchd. |
| 39 #define kServiceName "org.chromium.chromoting" | 39 #define kServiceName "org.chromium.chromoting" |
| 40 | |
| 41 #define kUpdateCompleteNotificationName kServiceName ".update_complete" | |
| 40 #define kConfigDir "/Library/PrivilegedHelperTools/" | 42 #define kConfigDir "/Library/PrivilegedHelperTools/" |
| 41 | 43 |
| 42 // This helper script is used to get the installed host version. | 44 // This helper script is used to get the installed host version. |
| 43 const char kHostHelperScript[] = kConfigDir kServiceName ".me2me.sh"; | 45 const char kHostHelperScript[] = kConfigDir kServiceName ".me2me.sh"; |
| 44 | 46 |
| 45 // Use a single configuration file, instead of separate "auth" and "host" files. | 47 // Use a single configuration file, instead of separate "auth" and "host" files. |
| 46 // This is because the SetConfigAndStart() API only provides a single | 48 // This is because the SetConfigAndStart() API only provides a single |
| 47 // dictionary, and splitting this into two dictionaries would require | 49 // dictionary, and splitting this into two dictionaries would require |
| 48 // knowledge of which keys belong in which files. | 50 // knowledge of which keys belong in which files. |
| 49 const char kHostConfigFile[] = kConfigDir kServiceName ".json"; | 51 const char kHostConfigFile[] = kConfigDir kServiceName ".json"; |
| 50 | 52 |
| 51 const int kPrefPaneWaitRetryLimit = 60; | |
| 52 const int kPrefPaneWaitTimeout = 1000; | |
| 53 | |
| 54 class DaemonControllerMac : public remoting::DaemonController { | 53 class DaemonControllerMac : public remoting::DaemonController { |
| 55 public: | 54 public: |
| 56 DaemonControllerMac(); | 55 DaemonControllerMac(); |
| 57 virtual ~DaemonControllerMac(); | 56 virtual ~DaemonControllerMac(); |
| 58 | 57 |
| 59 virtual State GetState() OVERRIDE; | 58 virtual State GetState() OVERRIDE; |
| 60 virtual void GetConfig(const GetConfigCallback& callback) OVERRIDE; | 59 virtual void GetConfig(const GetConfigCallback& callback) OVERRIDE; |
| 61 virtual void SetConfigAndStart( | 60 virtual void SetConfigAndStart( |
| 62 scoped_ptr<base::DictionaryValue> config, | 61 scoped_ptr<base::DictionaryValue> config, |
| 63 const CompletionCallback& done_callback) OVERRIDE; | 62 const CompletionCallback& done_callback) OVERRIDE; |
| 64 virtual void UpdateConfig(scoped_ptr<base::DictionaryValue> config, | 63 virtual void UpdateConfig(scoped_ptr<base::DictionaryValue> config, |
| 65 const CompletionCallback& done_callback) OVERRIDE; | 64 const CompletionCallback& done_callback) OVERRIDE; |
| 66 virtual void Stop(const CompletionCallback& done_callback) OVERRIDE; | 65 virtual void Stop(const CompletionCallback& done_callback) OVERRIDE; |
| 67 virtual void SetWindow(void* window_handle) OVERRIDE; | 66 virtual void SetWindow(void* window_handle) OVERRIDE; |
| 68 virtual void GetVersion(const GetVersionCallback& done_callback) OVERRIDE; | 67 virtual void GetVersion(const GetVersionCallback& done_callback) OVERRIDE; |
| 69 | 68 |
| 70 private: | 69 private: |
| 71 void DoGetConfig(const GetConfigCallback& callback); | 70 void DoGetConfig(const GetConfigCallback& callback); |
| 72 void DoGetVersion(const GetVersionCallback& callback); | 71 void DoGetVersion(const GetVersionCallback& callback); |
| 73 void DoSetConfigAndStart(scoped_ptr<base::DictionaryValue> config, | 72 void DoSetConfigAndStart(scoped_ptr<base::DictionaryValue> config, |
| 74 const CompletionCallback& done_callback); | 73 const CompletionCallback& done_callback); |
| 75 void DoUpdateConfig(scoped_ptr<base::DictionaryValue> config, | 74 void DoUpdateConfig(scoped_ptr<base::DictionaryValue> config, |
| 76 const CompletionCallback& done_callback); | 75 const CompletionCallback& done_callback); |
| 77 void DoStop(const CompletionCallback& done_callback); | 76 void DoStop(const CompletionCallback& done_callback); |
| 78 void NotifyOnState(DaemonController::State state, | 77 |
| 79 const CompletionCallback& done_callback, | 78 void ShowPreferencePane(const std::string& config_data, |
| 80 int tries_remaining, | 79 const CompletionCallback& done_callback); |
| 81 const base::TimeDelta& sleep); | 80 void RegisterForPreferencePaneNotifications( |
| 82 bool ShowPreferencePane(const std::string& config_data); | 81 const CompletionCallback &done_callback); |
| 82 void PreferencePaneCallbackDelegate(CFStringRef name, | |
| 83 CFDictionaryRef user_info); | |
| 84 static bool DoShowPreferencePane(const std::string& config_data); | |
| 85 static void PreferencePaneCallback(CFNotificationCenterRef center, | |
| 86 void* observer, | |
| 87 CFStringRef name, | |
| 88 const void* object, | |
| 89 CFDictionaryRef user_info); | |
| 83 | 90 |
| 84 base::Thread auth_thread_; | 91 base::Thread auth_thread_; |
| 92 CompletionCallback current_callback_; | |
| 85 | 93 |
| 86 DISALLOW_COPY_AND_ASSIGN(DaemonControllerMac); | 94 DISALLOW_COPY_AND_ASSIGN(DaemonControllerMac); |
| 87 }; | 95 }; |
| 88 | 96 |
| 89 DaemonControllerMac::DaemonControllerMac() | 97 DaemonControllerMac::DaemonControllerMac() |
| 90 : auth_thread_("Auth thread") { | 98 : auth_thread_("Auth thread") { |
| 91 auth_thread_.Start(); | 99 auth_thread_.Start(); |
| 92 } | 100 } |
| 93 | 101 |
| 94 DaemonControllerMac::~DaemonControllerMac() { | 102 DaemonControllerMac::~DaemonControllerMac() { |
| 95 auth_thread_.Stop(); | 103 auth_thread_.Stop(); |
| 104 CFNotificationCenterRemoveObserver( | |
| 105 CFNotificationCenterGetDistributedCenter(), | |
| 106 this, | |
| 107 CFSTR(kUpdateCompleteNotificationName), | |
| 108 NULL); | |
| 96 } | 109 } |
| 97 | 110 |
| 98 DaemonController::State DaemonControllerMac::GetState() { | 111 DaemonController::State DaemonControllerMac::GetState() { |
| 99 pid_t job_pid = base::mac::PIDForJob(kServiceName); | 112 pid_t job_pid = base::mac::PIDForJob(kServiceName); |
| 100 if (job_pid < 0) { | 113 if (job_pid < 0) { |
| 101 return DaemonController::STATE_NOT_INSTALLED; | 114 return DaemonController::STATE_NOT_INSTALLED; |
| 102 } else if (job_pid == 0) { | 115 } else if (job_pid == 0) { |
| 103 // Service is stopped, or a start attempt failed. | 116 // Service is stopped, or a start attempt failed. |
| 104 return DaemonController::STATE_STOPPED; | 117 return DaemonController::STATE_STOPPED; |
| 105 } else { | 118 } else { |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 189 } | 202 } |
| 190 } | 203 } |
| 191 callback.Run(version); | 204 callback.Run(version); |
| 192 } | 205 } |
| 193 | 206 |
| 194 void DaemonControllerMac::DoSetConfigAndStart( | 207 void DaemonControllerMac::DoSetConfigAndStart( |
| 195 scoped_ptr<base::DictionaryValue> config, | 208 scoped_ptr<base::DictionaryValue> config, |
| 196 const CompletionCallback& done_callback) { | 209 const CompletionCallback& done_callback) { |
| 197 std::string config_data; | 210 std::string config_data; |
| 198 base::JSONWriter::Write(config.get(), &config_data); | 211 base::JSONWriter::Write(config.get(), &config_data); |
| 199 | 212 ShowPreferencePane(config_data, done_callback); |
| 200 bool result = ShowPreferencePane(config_data); | |
| 201 | |
| 202 if (!result) { | |
| 203 done_callback.Run(RESULT_FAILED); | |
| 204 } | |
| 205 | |
| 206 // TODO(jamiewalch): Replace this with something a bit more robust | |
| 207 NotifyOnState(DaemonController::STATE_STARTED, | |
| 208 done_callback, | |
| 209 kPrefPaneWaitRetryLimit, | |
| 210 base::TimeDelta::FromMilliseconds(kPrefPaneWaitTimeout)); | |
| 211 } | 213 } |
| 212 | 214 |
| 213 void DaemonControllerMac::DoUpdateConfig( | 215 void DaemonControllerMac::DoUpdateConfig( |
| 214 scoped_ptr<base::DictionaryValue> config, | 216 scoped_ptr<base::DictionaryValue> config, |
| 215 const CompletionCallback& done_callback) { | 217 const CompletionCallback& done_callback) { |
| 216 FilePath config_file_path(kHostConfigFile); | 218 FilePath config_file_path(kHostConfigFile); |
| 217 JsonHostConfig config_file(config_file_path); | 219 JsonHostConfig config_file(config_file_path); |
| 218 if (!config_file.Read()) { | 220 if (!config_file.Read()) { |
| 219 done_callback.Run(RESULT_FAILED); | 221 done_callback.Run(RESULT_FAILED); |
| 220 return; | 222 return; |
| 221 } | 223 } |
| 222 for (DictionaryValue::key_iterator key(config->begin_keys()); | 224 for (DictionaryValue::key_iterator key(config->begin_keys()); |
| 223 key != config->end_keys(); ++key) { | 225 key != config->end_keys(); ++key) { |
| 224 std::string value; | 226 std::string value; |
| 225 if (!config->GetString(*key, &value)) { | 227 if (!config->GetString(*key, &value)) { |
| 226 LOG(ERROR) << *key << " is not a string."; | 228 LOG(ERROR) << *key << " is not a string."; |
| 227 done_callback.Run(RESULT_FAILED); | 229 done_callback.Run(RESULT_FAILED); |
| 228 return; | 230 return; |
| 229 } | 231 } |
| 230 config_file.SetString(*key, value); | 232 config_file.SetString(*key, value); |
| 231 } | 233 } |
| 232 | 234 |
| 233 std::string config_data = config_file.GetSerializedData(); | 235 std::string config_data = config_file.GetSerializedData(); |
| 234 bool success = ShowPreferencePane(config_data); | 236 ShowPreferencePane(config_data, done_callback); |
| 235 | |
| 236 done_callback.Run(success ? RESULT_OK : RESULT_FAILED); | |
| 237 } | 237 } |
| 238 | 238 |
| 239 bool DaemonControllerMac::ShowPreferencePane(const std::string& config_data) { | 239 void DaemonControllerMac::ShowPreferencePane( |
| 240 const std::string& config_data, const CompletionCallback& done_callback) { | |
| 241 if (DoShowPreferencePane(config_data)) { | |
| 242 RegisterForPreferencePaneNotifications(done_callback); | |
| 243 } else { | |
| 244 done_callback.Run(RESULT_FAILED); | |
| 245 } | |
| 246 } | |
| 247 | |
| 248 bool DaemonControllerMac::DoShowPreferencePane(const std::string& config_data) { | |
| 240 if (!config_data.empty()) { | 249 if (!config_data.empty()) { |
| 241 FilePath config_path; | 250 FilePath config_path; |
| 242 if (!file_util::GetTempDir(&config_path)) { | 251 if (!file_util::GetTempDir(&config_path)) { |
| 243 LOG(ERROR) << "Failed to get filename for saving configuration data."; | 252 LOG(ERROR) << "Failed to get filename for saving configuration data."; |
| 244 return false; | 253 return false; |
| 245 } | 254 } |
| 246 config_path = config_path.Append(kServiceName ".json"); | 255 config_path = config_path.Append(kServiceName ".json"); |
| 247 | 256 |
| 248 int written = file_util::WriteFile(config_path, config_data.data(), | 257 int written = file_util::WriteFile(config_path, config_data.data(), |
| 249 config_data.size()); | 258 config_data.size()); |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 277 } | 286 } |
| 278 | 287 |
| 279 CFNotificationCenterRef center = | 288 CFNotificationCenterRef center = |
| 280 CFNotificationCenterGetDistributedCenter(); | 289 CFNotificationCenterGetDistributedCenter(); |
| 281 CFNotificationCenterPostNotification(center, CFSTR(kServiceName), NULL, NULL, | 290 CFNotificationCenterPostNotification(center, CFSTR(kServiceName), NULL, NULL, |
| 282 TRUE); | 291 TRUE); |
| 283 return true; | 292 return true; |
| 284 } | 293 } |
| 285 | 294 |
| 286 void DaemonControllerMac::DoStop(const CompletionCallback& done_callback) { | 295 void DaemonControllerMac::DoStop(const CompletionCallback& done_callback) { |
| 287 if (!ShowPreferencePane("")) { | 296 ShowPreferencePane("", done_callback); |
| 288 done_callback.Run(RESULT_FAILED); | 297 } |
| 298 | |
| 299 // CFNotificationCenterAddObserver ties the thread on which distributed | |
| 300 // notifications are received to the one on which it is first called. | |
| 301 // This is safe because HostNPScriptObject::InvokeAsyncResultCallback | |
| 302 // bounces the invocation to the correct thread, so it doesn't matter | |
| 303 // which thread CompletionCallbacks are called on. | |
| 304 void DaemonControllerMac::RegisterForPreferencePaneNotifications( | |
| 305 const CompletionCallback& done_callback) { | |
| 306 // We can only have one callback registered at a time. This is enforced by the | |
| 307 // UX flow of the web-app. | |
| 308 DCHECK(current_callback_.is_null()); | |
|
Lambros
2012/05/15 18:55:35
DCHECK seems too strong. Maybe the user could hit
Jamie
2012/05/15 20:53:50
I think DCHECK is correct. If the user refreshes t
| |
| 309 current_callback_ = done_callback; | |
| 310 | |
| 311 CFNotificationCenterAddObserver( | |
| 312 CFNotificationCenterGetDistributedCenter(), | |
| 313 this, | |
| 314 &DaemonControllerMac::PreferencePaneCallback, | |
| 315 CFSTR(kUpdateCompleteNotificationName), | |
| 316 NULL, | |
| 317 CFNotificationSuspensionBehaviorDeliverImmediately); | |
| 318 } | |
| 319 | |
| 320 void DaemonControllerMac::PreferencePaneCallbackDelegate( | |
| 321 CFStringRef name, | |
| 322 CFDictionaryRef result_dictionary) { | |
| 323 if (CFStringCompare(name, CFSTR(kUpdateCompleteNotificationName), 0) != | |
| 324 kCFCompareEqualTo) { | |
| 325 LOG(WARNING) << "Ignoring unexpected notification: " << name; | |
| 289 return; | 326 return; |
| 290 } | 327 } |
| 291 | 328 AsyncResult result = RESULT_FAILED; |
| 292 // TODO(jamiewalch): Replace this with something a bit more robust | 329 CFStringRef result_string = |
| 293 NotifyOnState(DaemonController::STATE_STOPPED, | 330 base::mac::GetValueFromDictionary<CFStringRef>(result_dictionary, |
| 294 done_callback, | 331 CFSTR("result")); |
| 295 kPrefPaneWaitRetryLimit, | 332 if (CFStringCompare(result_string, CFSTR("OK"), 0) == kCFCompareEqualTo) { |
| 296 base::TimeDelta::FromMilliseconds(kPrefPaneWaitTimeout)); | 333 result = RESULT_OK; |
| 334 } else if (CFStringCompare(result_string, | |
| 335 CFSTR("CANCEL"), 0) == kCFCompareEqualTo) { | |
| 336 result = RESULT_CANCELLED; | |
| 337 } | |
| 338 current_callback_.Run(result); | |
|
Lambros
2012/05/15 18:55:35
DCHECK current_callback_ ?
Jamie
2012/05/15 20:53:50
Done.
| |
| 339 current_callback_.Reset(); | |
| 340 CFNotificationCenterRemoveObserver( | |
| 341 CFNotificationCenterGetDistributedCenter(), | |
| 342 this, | |
| 343 CFSTR(kUpdateCompleteNotificationName), | |
| 344 NULL); | |
| 297 } | 345 } |
| 298 | 346 |
| 299 void DaemonControllerMac::NotifyOnState( | 347 void DaemonControllerMac::PreferencePaneCallback(CFNotificationCenterRef center, |
| 300 DaemonController::State state, | 348 void* observer, |
| 301 const CompletionCallback& done_callback, | 349 CFStringRef name, |
| 302 int tries_remaining, | 350 const void* object, |
| 303 const base::TimeDelta& sleep) { | 351 CFDictionaryRef user_info) { |
| 304 if (GetState() == state) { | 352 DaemonControllerMac* self = reinterpret_cast<DaemonControllerMac*>(observer); |
| 305 done_callback.Run(RESULT_OK); | 353 if (self) { |
| 306 } else if (tries_remaining == 0) { | 354 self->PreferencePaneCallbackDelegate(name, user_info); |
| 307 done_callback.Run(RESULT_FAILED); | |
| 308 } else { | 355 } else { |
| 309 auth_thread_.message_loop_proxy()->PostDelayedTask( | 356 LOG(WARNING) << "Ignoring notification with NULL observer: " << name; |
| 310 FROM_HERE, | |
| 311 base::Bind(&DaemonControllerMac::NotifyOnState, | |
| 312 base::Unretained(this), | |
| 313 state, | |
| 314 done_callback, | |
| 315 tries_remaining - 1, | |
| 316 sleep), | |
| 317 sleep); | |
| 318 } | 357 } |
| 319 } | 358 } |
| 320 | 359 |
| 321 } // namespace | 360 } // namespace |
| 322 | 361 |
| 323 scoped_ptr<DaemonController> remoting::DaemonController::Create() { | 362 scoped_ptr<DaemonController> remoting::DaemonController::Create() { |
| 324 return scoped_ptr<DaemonController>(new DaemonControllerMac()); | 363 return scoped_ptr<DaemonController>(new DaemonControllerMac()); |
| 325 } | 364 } |
| 326 | 365 |
| 327 } // namespace remoting | 366 } // namespace remoting |
| OLD | NEW |