| 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 // Use separate named notifications for success and failure because sandboxed |
| 42 // components can't include a dictionary when sending distributed notifications. |
| 43 // The preferences panel is not yet sandboxed, but err on the side of caution. |
| 44 #define kUpdateSucceededNotificationName kServiceName ".update_succeeded" |
| 45 #define kUpdateFailedNotificationName kServiceName ".update_failed" |
| 46 |
| 40 #define kConfigDir "/Library/PrivilegedHelperTools/" | 47 #define kConfigDir "/Library/PrivilegedHelperTools/" |
| 41 | 48 |
| 42 // This helper script is used to get the installed host version. | 49 // This helper script is used to get the installed host version. |
| 43 const char kHostHelperScript[] = kConfigDir kServiceName ".me2me.sh"; | 50 const char kHostHelperScript[] = kConfigDir kServiceName ".me2me.sh"; |
| 44 | 51 |
| 45 // Use a single configuration file, instead of separate "auth" and "host" files. | 52 // Use a single configuration file, instead of separate "auth" and "host" files. |
| 46 // This is because the SetConfigAndStart() API only provides a single | 53 // This is because the SetConfigAndStart() API only provides a single |
| 47 // dictionary, and splitting this into two dictionaries would require | 54 // dictionary, and splitting this into two dictionaries would require |
| 48 // knowledge of which keys belong in which files. | 55 // knowledge of which keys belong in which files. |
| 49 const char kHostConfigFile[] = kConfigDir kServiceName ".json"; | 56 const char kHostConfigFile[] = kConfigDir kServiceName ".json"; |
| 50 | 57 |
| 51 const int kPrefPaneWaitRetryLimit = 60; | |
| 52 const int kPrefPaneWaitTimeout = 1000; | |
| 53 | |
| 54 class DaemonControllerMac : public remoting::DaemonController { | 58 class DaemonControllerMac : public remoting::DaemonController { |
| 55 public: | 59 public: |
| 56 DaemonControllerMac(); | 60 DaemonControllerMac(); |
| 57 virtual ~DaemonControllerMac(); | 61 virtual ~DaemonControllerMac(); |
| 58 | 62 |
| 59 virtual State GetState() OVERRIDE; | 63 virtual State GetState() OVERRIDE; |
| 60 virtual void GetConfig(const GetConfigCallback& callback) OVERRIDE; | 64 virtual void GetConfig(const GetConfigCallback& callback) OVERRIDE; |
| 61 virtual void SetConfigAndStart( | 65 virtual void SetConfigAndStart( |
| 62 scoped_ptr<base::DictionaryValue> config, | 66 scoped_ptr<base::DictionaryValue> config, |
| 63 const CompletionCallback& done_callback) OVERRIDE; | 67 const CompletionCallback& done_callback) OVERRIDE; |
| 64 virtual void UpdateConfig(scoped_ptr<base::DictionaryValue> config, | 68 virtual void UpdateConfig(scoped_ptr<base::DictionaryValue> config, |
| 65 const CompletionCallback& done_callback) OVERRIDE; | 69 const CompletionCallback& done_callback) OVERRIDE; |
| 66 virtual void Stop(const CompletionCallback& done_callback) OVERRIDE; | 70 virtual void Stop(const CompletionCallback& done_callback) OVERRIDE; |
| 67 virtual void SetWindow(void* window_handle) OVERRIDE; | 71 virtual void SetWindow(void* window_handle) OVERRIDE; |
| 68 virtual void GetVersion(const GetVersionCallback& done_callback) OVERRIDE; | 72 virtual void GetVersion(const GetVersionCallback& done_callback) OVERRIDE; |
| 69 | 73 |
| 70 private: | 74 private: |
| 71 void DoGetConfig(const GetConfigCallback& callback); | 75 void DoGetConfig(const GetConfigCallback& callback); |
| 72 void DoGetVersion(const GetVersionCallback& callback); | 76 void DoGetVersion(const GetVersionCallback& callback); |
| 73 void DoSetConfigAndStart(scoped_ptr<base::DictionaryValue> config, | 77 void DoSetConfigAndStart(scoped_ptr<base::DictionaryValue> config, |
| 74 const CompletionCallback& done_callback); | 78 const CompletionCallback& done_callback); |
| 75 void DoUpdateConfig(scoped_ptr<base::DictionaryValue> config, | 79 void DoUpdateConfig(scoped_ptr<base::DictionaryValue> config, |
| 76 const CompletionCallback& done_callback); | 80 const CompletionCallback& done_callback); |
| 77 void DoStop(const CompletionCallback& done_callback); | 81 void DoStop(const CompletionCallback& done_callback); |
| 78 void NotifyOnState(DaemonController::State state, | 82 |
| 79 const CompletionCallback& done_callback, | 83 void ShowPreferencePane(const std::string& config_data, |
| 80 int tries_remaining, | 84 const CompletionCallback& done_callback); |
| 81 const base::TimeDelta& sleep); | 85 void RegisterForPreferencePaneNotifications( |
| 82 bool ShowPreferencePane(const std::string& config_data); | 86 const CompletionCallback &done_callback); |
| 87 void DeregisterForPreferencePaneNotifications(); |
| 88 void PreferencePaneCallbackDelegate(CFStringRef name); |
| 89 static bool DoShowPreferencePane(const std::string& config_data); |
| 90 static void PreferencePaneCallback(CFNotificationCenterRef center, |
| 91 void* observer, |
| 92 CFStringRef name, |
| 93 const void* object, |
| 94 CFDictionaryRef user_info); |
| 83 | 95 |
| 84 base::Thread auth_thread_; | 96 base::Thread auth_thread_; |
| 97 CompletionCallback current_callback_; |
| 85 | 98 |
| 86 DISALLOW_COPY_AND_ASSIGN(DaemonControllerMac); | 99 DISALLOW_COPY_AND_ASSIGN(DaemonControllerMac); |
| 87 }; | 100 }; |
| 88 | 101 |
| 89 DaemonControllerMac::DaemonControllerMac() | 102 DaemonControllerMac::DaemonControllerMac() |
| 90 : auth_thread_("Auth thread") { | 103 : auth_thread_("Auth thread") { |
| 91 auth_thread_.Start(); | 104 auth_thread_.Start(); |
| 92 } | 105 } |
| 93 | 106 |
| 94 DaemonControllerMac::~DaemonControllerMac() { | 107 DaemonControllerMac::~DaemonControllerMac() { |
| 95 auth_thread_.Stop(); | 108 auth_thread_.Stop(); |
| 109 DeregisterForPreferencePaneNotifications(); |
| 110 } |
| 111 |
| 112 void DaemonControllerMac::DeregisterForPreferencePaneNotifications() { |
| 113 CFNotificationCenterRemoveObserver( |
| 114 CFNotificationCenterGetDistributedCenter(), |
| 115 this, |
| 116 CFSTR(kUpdateSucceededNotificationName), |
| 117 NULL); |
| 118 CFNotificationCenterRemoveObserver( |
| 119 CFNotificationCenterGetDistributedCenter(), |
| 120 this, |
| 121 CFSTR(kUpdateFailedNotificationName), |
| 122 NULL); |
| 96 } | 123 } |
| 97 | 124 |
| 98 DaemonController::State DaemonControllerMac::GetState() { | 125 DaemonController::State DaemonControllerMac::GetState() { |
| 99 pid_t job_pid = base::mac::PIDForJob(kServiceName); | 126 pid_t job_pid = base::mac::PIDForJob(kServiceName); |
| 100 if (job_pid < 0) { | 127 if (job_pid < 0) { |
| 101 return DaemonController::STATE_NOT_INSTALLED; | 128 return DaemonController::STATE_NOT_INSTALLED; |
| 102 } else if (job_pid == 0) { | 129 } else if (job_pid == 0) { |
| 103 // Service is stopped, or a start attempt failed. | 130 // Service is stopped, or a start attempt failed. |
| 104 return DaemonController::STATE_STOPPED; | 131 return DaemonController::STATE_STOPPED; |
| 105 } else { | 132 } else { |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 189 } | 216 } |
| 190 } | 217 } |
| 191 callback.Run(version); | 218 callback.Run(version); |
| 192 } | 219 } |
| 193 | 220 |
| 194 void DaemonControllerMac::DoSetConfigAndStart( | 221 void DaemonControllerMac::DoSetConfigAndStart( |
| 195 scoped_ptr<base::DictionaryValue> config, | 222 scoped_ptr<base::DictionaryValue> config, |
| 196 const CompletionCallback& done_callback) { | 223 const CompletionCallback& done_callback) { |
| 197 std::string config_data; | 224 std::string config_data; |
| 198 base::JSONWriter::Write(config.get(), &config_data); | 225 base::JSONWriter::Write(config.get(), &config_data); |
| 199 | 226 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 } | 227 } |
| 212 | 228 |
| 213 void DaemonControllerMac::DoUpdateConfig( | 229 void DaemonControllerMac::DoUpdateConfig( |
| 214 scoped_ptr<base::DictionaryValue> config, | 230 scoped_ptr<base::DictionaryValue> config, |
| 215 const CompletionCallback& done_callback) { | 231 const CompletionCallback& done_callback) { |
| 216 FilePath config_file_path(kHostConfigFile); | 232 FilePath config_file_path(kHostConfigFile); |
| 217 JsonHostConfig config_file(config_file_path); | 233 JsonHostConfig config_file(config_file_path); |
| 218 if (!config_file.Read()) { | 234 if (!config_file.Read()) { |
| 219 done_callback.Run(RESULT_FAILED); | 235 done_callback.Run(RESULT_FAILED); |
| 220 return; | 236 return; |
| 221 } | 237 } |
| 222 for (DictionaryValue::key_iterator key(config->begin_keys()); | 238 for (DictionaryValue::key_iterator key(config->begin_keys()); |
| 223 key != config->end_keys(); ++key) { | 239 key != config->end_keys(); ++key) { |
| 224 std::string value; | 240 std::string value; |
| 225 if (!config->GetString(*key, &value)) { | 241 if (!config->GetString(*key, &value)) { |
| 226 LOG(ERROR) << *key << " is not a string."; | 242 LOG(ERROR) << *key << " is not a string."; |
| 227 done_callback.Run(RESULT_FAILED); | 243 done_callback.Run(RESULT_FAILED); |
| 228 return; | 244 return; |
| 229 } | 245 } |
| 230 config_file.SetString(*key, value); | 246 config_file.SetString(*key, value); |
| 231 } | 247 } |
| 232 | 248 |
| 233 std::string config_data = config_file.GetSerializedData(); | 249 std::string config_data = config_file.GetSerializedData(); |
| 234 bool success = ShowPreferencePane(config_data); | 250 ShowPreferencePane(config_data, done_callback); |
| 235 | |
| 236 done_callback.Run(success ? RESULT_OK : RESULT_FAILED); | |
| 237 } | 251 } |
| 238 | 252 |
| 239 bool DaemonControllerMac::ShowPreferencePane(const std::string& config_data) { | 253 void DaemonControllerMac::ShowPreferencePane( |
| 254 const std::string& config_data, const CompletionCallback& done_callback) { |
| 255 if (DoShowPreferencePane(config_data)) { |
| 256 RegisterForPreferencePaneNotifications(done_callback); |
| 257 } else { |
| 258 done_callback.Run(RESULT_FAILED); |
| 259 } |
| 260 } |
| 261 |
| 262 bool DaemonControllerMac::DoShowPreferencePane(const std::string& config_data) { |
| 240 if (!config_data.empty()) { | 263 if (!config_data.empty()) { |
| 241 FilePath config_path; | 264 FilePath config_path; |
| 242 if (!file_util::GetTempDir(&config_path)) { | 265 if (!file_util::GetTempDir(&config_path)) { |
| 243 LOG(ERROR) << "Failed to get filename for saving configuration data."; | 266 LOG(ERROR) << "Failed to get filename for saving configuration data."; |
| 244 return false; | 267 return false; |
| 245 } | 268 } |
| 246 config_path = config_path.Append(kServiceName ".json"); | 269 config_path = config_path.Append(kServiceName ".json"); |
| 247 | 270 |
| 248 int written = file_util::WriteFile(config_path, config_data.data(), | 271 int written = file_util::WriteFile(config_path, config_data.data(), |
| 249 config_data.size()); | 272 config_data.size()); |
| (...skipping 27 matching lines...) Expand all Loading... |
| 277 } | 300 } |
| 278 | 301 |
| 279 CFNotificationCenterRef center = | 302 CFNotificationCenterRef center = |
| 280 CFNotificationCenterGetDistributedCenter(); | 303 CFNotificationCenterGetDistributedCenter(); |
| 281 CFNotificationCenterPostNotification(center, CFSTR(kServiceName), NULL, NULL, | 304 CFNotificationCenterPostNotification(center, CFSTR(kServiceName), NULL, NULL, |
| 282 TRUE); | 305 TRUE); |
| 283 return true; | 306 return true; |
| 284 } | 307 } |
| 285 | 308 |
| 286 void DaemonControllerMac::DoStop(const CompletionCallback& done_callback) { | 309 void DaemonControllerMac::DoStop(const CompletionCallback& done_callback) { |
| 287 if (!ShowPreferencePane("")) { | 310 ShowPreferencePane("", done_callback); |
| 288 done_callback.Run(RESULT_FAILED); | 311 } |
| 312 |
| 313 // CFNotificationCenterAddObserver ties the thread on which distributed |
| 314 // notifications are received to the one on which it is first called. |
| 315 // This is safe because HostNPScriptObject::InvokeAsyncResultCallback |
| 316 // bounces the invocation to the correct thread, so it doesn't matter |
| 317 // which thread CompletionCallbacks are called on. |
| 318 void DaemonControllerMac::RegisterForPreferencePaneNotifications( |
| 319 const CompletionCallback& done_callback) { |
| 320 // We can only have one callback registered at a time. This is enforced by the |
| 321 // UX flow of the web-app. |
| 322 DCHECK(current_callback_.is_null()); |
| 323 current_callback_ = done_callback; |
| 324 |
| 325 CFNotificationCenterAddObserver( |
| 326 CFNotificationCenterGetDistributedCenter(), |
| 327 this, |
| 328 &DaemonControllerMac::PreferencePaneCallback, |
| 329 CFSTR(kUpdateSucceededNotificationName), |
| 330 NULL, |
| 331 CFNotificationSuspensionBehaviorDeliverImmediately); |
| 332 CFNotificationCenterAddObserver( |
| 333 CFNotificationCenterGetDistributedCenter(), |
| 334 this, |
| 335 &DaemonControllerMac::PreferencePaneCallback, |
| 336 CFSTR(kUpdateFailedNotificationName), |
| 337 NULL, |
| 338 CFNotificationSuspensionBehaviorDeliverImmediately); |
| 339 } |
| 340 |
| 341 void DaemonControllerMac::PreferencePaneCallbackDelegate(CFStringRef name) { |
| 342 AsyncResult result = RESULT_FAILED; |
| 343 if (CFStringCompare(name, CFSTR(kUpdateSucceededNotificationName), 0) == |
| 344 kCFCompareEqualTo) { |
| 345 result = RESULT_OK; |
| 346 } else if (CFStringCompare(name, CFSTR(kUpdateFailedNotificationName), 0) == |
| 347 kCFCompareEqualTo) { |
| 348 result = RESULT_CANCELLED; |
| 349 } else { |
| 350 LOG(WARNING) << "Ignoring unexpected notification: " << name; |
| 289 return; | 351 return; |
| 290 } | 352 } |
| 291 | 353 DCHECK(!current_callback_.is_null()); |
| 292 // TODO(jamiewalch): Replace this with something a bit more robust | 354 current_callback_.Run(result); |
| 293 NotifyOnState(DaemonController::STATE_STOPPED, | 355 current_callback_.Reset(); |
| 294 done_callback, | 356 DeregisterForPreferencePaneNotifications(); |
| 295 kPrefPaneWaitRetryLimit, | |
| 296 base::TimeDelta::FromMilliseconds(kPrefPaneWaitTimeout)); | |
| 297 } | 357 } |
| 298 | 358 |
| 299 void DaemonControllerMac::NotifyOnState( | 359 void DaemonControllerMac::PreferencePaneCallback(CFNotificationCenterRef center, |
| 300 DaemonController::State state, | 360 void* observer, |
| 301 const CompletionCallback& done_callback, | 361 CFStringRef name, |
| 302 int tries_remaining, | 362 const void* object, |
| 303 const base::TimeDelta& sleep) { | 363 CFDictionaryRef user_info) { |
| 304 if (GetState() == state) { | 364 DaemonControllerMac* self = reinterpret_cast<DaemonControllerMac*>(observer); |
| 305 done_callback.Run(RESULT_OK); | 365 if (self) { |
| 306 } else if (tries_remaining == 0) { | 366 self->PreferencePaneCallbackDelegate(name); |
| 307 done_callback.Run(RESULT_FAILED); | |
| 308 } else { | 367 } else { |
| 309 auth_thread_.message_loop_proxy()->PostDelayedTask( | 368 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 } | 369 } |
| 319 } | 370 } |
| 320 | 371 |
| 321 } // namespace | 372 } // namespace |
| 322 | 373 |
| 323 scoped_ptr<DaemonController> remoting::DaemonController::Create() { | 374 scoped_ptr<DaemonController> remoting::DaemonController::Create() { |
| 324 return scoped_ptr<DaemonController>(new DaemonControllerMac()); | 375 return scoped_ptr<DaemonController>(new DaemonControllerMac()); |
| 325 } | 376 } |
| 326 | 377 |
| 327 } // namespace remoting | 378 } // namespace remoting |
| OLD | NEW |