Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(187)

Side by Side Diff: remoting/host/setup/daemon_controller_mac.mm

Issue 23606019: Refactor the daemon controller so that the callbacks are called on the caller thread. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: fix the license Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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/setup/daemon_controller.h"
6
7 #include <launch.h>
8 #include <stdio.h>
9 #include <sys/types.h>
10
11 #include "base/basictypes.h"
12 #include "base/bind.h"
13 #include "base/compiler_specific.h"
14 #include "base/file_util.h"
15 #include "base/files/file_path.h"
16 #include "base/json/json_writer.h"
17 #include "base/logging.h"
18 #include "base/mac/foundation_util.h"
19 #include "base/mac/launchd.h"
20 #include "base/mac/mac_logging.h"
21 #include "base/mac/mac_util.h"
22 #include "base/mac/scoped_launch_data.h"
23 #include "base/threading/thread.h"
24 #include "base/time/time.h"
25 #include "base/values.h"
26 #include "remoting/host/constants_mac.h"
27 #include "remoting/host/json_host_config.h"
28 #include "remoting/host/usage_stats_consent.h"
29
30 namespace remoting {
31
32 namespace {
33
34 class DaemonControllerMac : public remoting::DaemonController {
35 public:
36 DaemonControllerMac();
37 virtual ~DaemonControllerMac();
38
39 virtual State GetState() OVERRIDE;
40 virtual void GetConfig(const GetConfigCallback& callback) OVERRIDE;
41 virtual void SetConfigAndStart(
42 scoped_ptr<base::DictionaryValue> config,
43 bool consent,
44 const CompletionCallback& done) OVERRIDE;
45 virtual void UpdateConfig(scoped_ptr<base::DictionaryValue> config,
46 const CompletionCallback& done_callback) OVERRIDE;
47 virtual void Stop(const CompletionCallback& done_callback) OVERRIDE;
48 virtual void SetWindow(void* window_handle) OVERRIDE;
49 virtual void GetVersion(const GetVersionCallback& done_callback) OVERRIDE;
50 virtual void GetUsageStatsConsent(
51 const GetUsageStatsConsentCallback& callback) OVERRIDE;
52
53 private:
54 void DoGetConfig(const GetConfigCallback& callback);
55 void DoGetVersion(const GetVersionCallback& callback);
56 void DoSetConfigAndStart(scoped_ptr<base::DictionaryValue> config,
57 bool consent,
58 const CompletionCallback& done);
59 void DoUpdateConfig(scoped_ptr<base::DictionaryValue> config,
60 const CompletionCallback& done_callback);
61 void DoStop(const CompletionCallback& done_callback);
62 void DoGetUsageStatsConsent(const GetUsageStatsConsentCallback& callback);
63
64 void ShowPreferencePane(const std::string& config_data,
65 const CompletionCallback& done_callback);
66 void RegisterForPreferencePaneNotifications(
67 const CompletionCallback &done_callback);
68 void DeregisterForPreferencePaneNotifications();
69 void PreferencePaneCallbackDelegate(CFStringRef name);
70 static bool DoShowPreferencePane(const std::string& config_data);
71 static void PreferencePaneCallback(CFNotificationCenterRef center,
72 void* observer,
73 CFStringRef name,
74 const void* object,
75 CFDictionaryRef user_info);
76
77 base::Thread auth_thread_;
78 CompletionCallback current_callback_;
79
80 DISALLOW_COPY_AND_ASSIGN(DaemonControllerMac);
81 };
82
83 DaemonControllerMac::DaemonControllerMac()
84 : auth_thread_("Auth thread") {
85 auth_thread_.Start();
86 }
87
88 DaemonControllerMac::~DaemonControllerMac() {
89 auth_thread_.Stop();
90 DeregisterForPreferencePaneNotifications();
91 }
92
93 void DaemonControllerMac::DeregisterForPreferencePaneNotifications() {
94 CFNotificationCenterRemoveObserver(
95 CFNotificationCenterGetDistributedCenter(),
96 this,
97 CFSTR(UPDATE_SUCCEEDED_NOTIFICATION_NAME),
98 NULL);
99 CFNotificationCenterRemoveObserver(
100 CFNotificationCenterGetDistributedCenter(),
101 this,
102 CFSTR(UPDATE_FAILED_NOTIFICATION_NAME),
103 NULL);
104 }
105
106 DaemonController::State DaemonControllerMac::GetState() {
107 pid_t job_pid = base::mac::PIDForJob(kServiceName);
108 if (job_pid < 0) {
109 return DaemonController::STATE_NOT_INSTALLED;
110 } else if (job_pid == 0) {
111 // Service is stopped, or a start attempt failed.
112 return DaemonController::STATE_STOPPED;
113 } else {
114 return DaemonController::STATE_STARTED;
115 }
116 }
117
118 void DaemonControllerMac::GetConfig(const GetConfigCallback& callback) {
119 // base::Unretained() is safe, since this object owns the thread and therefore
120 // outlives it.
121 auth_thread_.message_loop_proxy()->PostTask(
122 FROM_HERE,
123 base::Bind(&DaemonControllerMac::DoGetConfig, base::Unretained(this),
124 callback));
125 }
126
127 void DaemonControllerMac::SetConfigAndStart(
128 scoped_ptr<base::DictionaryValue> config,
129 bool consent,
130 const CompletionCallback& done) {
131 auth_thread_.message_loop_proxy()->PostTask(
132 FROM_HERE, base::Bind(
133 &DaemonControllerMac::DoSetConfigAndStart, base::Unretained(this),
134 base::Passed(&config), consent, done));
135 }
136
137 void DaemonControllerMac::UpdateConfig(
138 scoped_ptr<base::DictionaryValue> config,
139 const CompletionCallback& done_callback) {
140 auth_thread_.message_loop()->PostTask(FROM_HERE, base::Bind(
141 &DaemonControllerMac::DoUpdateConfig, base::Unretained(this),
142 base::Passed(&config), done_callback));
143 }
144
145 void DaemonControllerMac::Stop(const CompletionCallback& done_callback) {
146 auth_thread_.message_loop_proxy()->PostTask(
147 FROM_HERE, base::Bind(
148 &DaemonControllerMac::DoStop, base::Unretained(this), done_callback));
149 }
150
151 void DaemonControllerMac::SetWindow(void* window_handle) {
152 // noop
153 }
154
155 void DaemonControllerMac::GetVersion(const GetVersionCallback& callback) {
156 auth_thread_.message_loop_proxy()->PostTask(
157 FROM_HERE,
158 base::Bind(&DaemonControllerMac::DoGetVersion, base::Unretained(this),
159 callback));
160 }
161
162 void DaemonControllerMac::GetUsageStatsConsent(
163 const GetUsageStatsConsentCallback& callback) {
164 auth_thread_.message_loop_proxy()->PostTask(
165 FROM_HERE,
166 base::Bind(&DaemonControllerMac::DoGetUsageStatsConsent,
167 base::Unretained(this), callback));
168 }
169
170 void DaemonControllerMac::DoGetConfig(const GetConfigCallback& callback) {
171 base::FilePath config_path(kHostConfigFilePath);
172 JsonHostConfig host_config(config_path);
173 scoped_ptr<base::DictionaryValue> config;
174
175 if (host_config.Read()) {
176 config.reset(new base::DictionaryValue());
177 std::string value;
178 if (host_config.GetString(kHostIdConfigPath, &value))
179 config.get()->SetString(kHostIdConfigPath, value);
180 if (host_config.GetString(kXmppLoginConfigPath, &value))
181 config.get()->SetString(kXmppLoginConfigPath, value);
182 }
183
184 callback.Run(config.Pass());
185 }
186
187 void DaemonControllerMac::DoGetVersion(const GetVersionCallback& callback) {
188 std::string version = "";
189 std::string command_line = remoting::kHostHelperScriptPath;
190 command_line += " --host-version";
191 FILE* script_output = popen(command_line.c_str(), "r");
192 if (script_output) {
193 char buffer[100];
194 char* result = fgets(buffer, sizeof(buffer), script_output);
195 pclose(script_output);
196 if (result) {
197 // The string is guaranteed to be null-terminated, but probably contains
198 // a newline character, which we don't want.
199 for (int i = 0; result[i]; ++i) {
200 if (result[i] < ' ') {
201 result[i] = 0;
202 break;
203 }
204 }
205 version = result;
206 }
207 }
208 callback.Run(version);
209 }
210
211 void DaemonControllerMac::DoSetConfigAndStart(
212 scoped_ptr<base::DictionaryValue> config,
213 bool consent,
214 const CompletionCallback& done) {
215 config->SetBoolean(kUsageStatsConsentConfigPath, consent);
216 std::string config_data;
217 base::JSONWriter::Write(config.get(), &config_data);
218 ShowPreferencePane(config_data, done);
219 }
220
221 void DaemonControllerMac::DoUpdateConfig(
222 scoped_ptr<base::DictionaryValue> config,
223 const CompletionCallback& done_callback) {
224 base::FilePath config_file_path(kHostConfigFilePath);
225 JsonHostConfig config_file(config_file_path);
226 if (!config_file.Read()) {
227 done_callback.Run(RESULT_FAILED);
228 return;
229 }
230 if (!config_file.CopyFrom(config.get())) {
231 LOG(ERROR) << "Failed to update configuration.";
232 done_callback.Run(RESULT_FAILED);
233 return;
234 }
235
236 std::string config_data = config_file.GetSerializedData();
237 ShowPreferencePane(config_data, done_callback);
238 }
239
240 void DaemonControllerMac::DoGetUsageStatsConsent(
241 const GetUsageStatsConsentCallback& callback) {
242 bool allowed = false;
243 base::FilePath config_file_path(kHostConfigFilePath);
244 JsonHostConfig host_config(config_file_path);
245 if (host_config.Read()) {
246 host_config.GetBoolean(kUsageStatsConsentConfigPath, &allowed);
247 }
248 // set_by_policy is not yet supported.
249 callback.Run(true, allowed, false /* set_by_policy */);
250 }
251
252 void DaemonControllerMac::ShowPreferencePane(
253 const std::string& config_data, const CompletionCallback& done_callback) {
254 if (DoShowPreferencePane(config_data)) {
255 RegisterForPreferencePaneNotifications(done_callback);
256 } else {
257 done_callback.Run(RESULT_FAILED);
258 }
259 }
260
261 bool DaemonControllerMac::DoShowPreferencePane(const std::string& config_data) {
262 if (!config_data.empty()) {
263 base::FilePath config_path;
264 if (!file_util::GetTempDir(&config_path)) {
265 LOG(ERROR) << "Failed to get filename for saving configuration data.";
266 return false;
267 }
268 config_path = config_path.Append(kHostConfigFileName);
269
270 int written = file_util::WriteFile(config_path, config_data.data(),
271 config_data.size());
272 if (written != static_cast<int>(config_data.size())) {
273 LOG(ERROR) << "Failed to save configuration data to: "
274 << config_path.value();
275 return false;
276 }
277 }
278
279 base::FilePath pane_path;
280 // TODO(lambroslambrou): Use NSPreferencePanesDirectory once we start
281 // building against SDK 10.6.
282 if (!base::mac::GetLocalDirectory(NSLibraryDirectory, &pane_path)) {
283 LOG(ERROR) << "Failed to get directory for local preference panes.";
284 return false;
285 }
286 pane_path = pane_path.Append("PreferencePanes").Append(kPrefPaneFileName);
287
288 FSRef pane_path_ref;
289 if (!base::mac::FSRefFromPath(pane_path.value(), &pane_path_ref)) {
290 LOG(ERROR) << "Failed to create FSRef";
291 return false;
292 }
293 OSStatus status = LSOpenFSRef(&pane_path_ref, NULL);
294 if (status != noErr) {
295 OSSTATUS_LOG(ERROR, status) << "LSOpenFSRef failed for path: "
296 << pane_path.value();
297 return false;
298 }
299
300 CFNotificationCenterRef center =
301 CFNotificationCenterGetDistributedCenter();
302 base::ScopedCFTypeRef<CFStringRef> service_name(CFStringCreateWithCString(
303 kCFAllocatorDefault, remoting::kServiceName, kCFStringEncodingUTF8));
304 CFNotificationCenterPostNotification(center, service_name, NULL, NULL,
305 TRUE);
306 return true;
307 }
308
309 void DaemonControllerMac::DoStop(const CompletionCallback& done_callback) {
310 ShowPreferencePane("", done_callback);
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(UPDATE_SUCCEEDED_NOTIFICATION_NAME),
330 NULL,
331 CFNotificationSuspensionBehaviorDeliverImmediately);
332 CFNotificationCenterAddObserver(
333 CFNotificationCenterGetDistributedCenter(),
334 this,
335 &DaemonControllerMac::PreferencePaneCallback,
336 CFSTR(UPDATE_FAILED_NOTIFICATION_NAME),
337 NULL,
338 CFNotificationSuspensionBehaviorDeliverImmediately);
339 }
340
341 void DaemonControllerMac::PreferencePaneCallbackDelegate(CFStringRef name) {
342 AsyncResult result = RESULT_FAILED;
343 if (CFStringCompare(name, CFSTR(UPDATE_SUCCEEDED_NOTIFICATION_NAME), 0) ==
344 kCFCompareEqualTo) {
345 result = RESULT_OK;
346 } else if (CFStringCompare(name, CFSTR(UPDATE_FAILED_NOTIFICATION_NAME), 0) ==
347 kCFCompareEqualTo) {
348 result = RESULT_FAILED;
349 } else {
350 LOG(WARNING) << "Ignoring unexpected notification: " << name;
351 return;
352 }
353 DCHECK(!current_callback_.is_null());
354 current_callback_.Run(result);
355 current_callback_.Reset();
356 DeregisterForPreferencePaneNotifications();
357 }
358
359 void DaemonControllerMac::PreferencePaneCallback(CFNotificationCenterRef center,
360 void* observer,
361 CFStringRef name,
362 const void* object,
363 CFDictionaryRef user_info) {
364 DaemonControllerMac* self = reinterpret_cast<DaemonControllerMac*>(observer);
365 if (self) {
366 self->PreferencePaneCallbackDelegate(name);
367 } else {
368 LOG(WARNING) << "Ignoring notification with NULL observer: " << name;
369 }
370 }
371
372 } // namespace
373
374 scoped_ptr<DaemonController> remoting::DaemonController::Create() {
375 return scoped_ptr<DaemonController>(new DaemonControllerMac());
376 }
377
378 } // namespace remoting
OLDNEW
« no previous file with comments | « remoting/host/setup/daemon_controller_linux.cc ('k') | remoting/host/setup/daemon_controller_win.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698