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

Side by Side Diff: remoting/host/plugin/daemon_controller_mac.cc

Issue 9837031: Implement DaemonController Start(), Stop() and GetStatus() for Mac. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Change service string Created 8 years, 9 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
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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>
8 #include <sys/types.h>
9
7 #include "base/basictypes.h" 10 #include "base/basictypes.h"
11 #include "base/bind.h"
8 #include "base/compiler_specific.h" 12 #include "base/compiler_specific.h"
13 #include "base/file_path.h"
14 #include "base/file_util.h"
15 #include "base/json/json_writer.h"
9 #include "base/logging.h" 16 #include "base/logging.h"
17 #include "base/mac/authorization_util.h"
18 #include "base/mac/launchd.h"
19 #include "base/mac/mac_logging.h"
20 #include "base/mac/scoped_authorizationref.h"
21 #include "base/mac/scoped_launch_data.h"
22 #include "base/synchronization/lock.h"
23 #include "base/threading/thread.h"
24 #include "base/values.h"
25 #include "remoting/host/json_host_config.h"
10 26
11 namespace remoting { 27 namespace remoting {
12 28
13 namespace { 29 namespace {
14 30
31 // The name of the Remoting Host service that is registered with launchd.
32 #define kServiceName "org.chromium.chromoting"
33 #define kConfigDir "/Library/PrivilegedHelperTools/"
dmac 2012/03/29 14:52:41 normally we wouldn't hardcode a path like this. Th
34
35 // This helper script is executed as root. It is passed a command-line option
36 // (--enable or --disable), which causes it to create or remove a trigger file.
37 // The trigger file (defined in the service's plist file) informs launchd
38 // whether the Host service should be running. Creating the trigger file causes
39 // launchd to immediately start the service. Deleting the trigger file has no
40 // immediate effect, but it prevents the service from being restarted if it
41 // becomes stopped.
42 const char kStartStopTool[] = kConfigDir kServiceName ".me2me.sh";
43
44 // Use a single configuration file, instead of separate "auth" and "host" files.
45 // This is because the SetConfigAndStart() API only provides a single
46 // dictionary, and splitting this into two dictionaries would require
47 // knowledge of which keys belong in which files.
48 const char kHostConfigFile[] = kConfigDir kServiceName ".json";
49
15 class DaemonControllerMac : public remoting::DaemonController { 50 class DaemonControllerMac : public remoting::DaemonController {
16 public: 51 public:
17 DaemonControllerMac(); 52 DaemonControllerMac();
53 virtual ~DaemonControllerMac();
18 54
19 virtual State GetState() OVERRIDE; 55 virtual State GetState() OVERRIDE;
20 virtual void GetConfig(const GetConfigCallback& callback) OVERRIDE; 56 virtual void GetConfig(const GetConfigCallback& callback) OVERRIDE;
21 virtual void SetConfigAndStart( 57 virtual void SetConfigAndStart(
22 scoped_ptr<base::DictionaryValue> config) OVERRIDE; 58 scoped_ptr<base::DictionaryValue> config) OVERRIDE;
23 virtual void SetPin(const std::string& pin) OVERRIDE; 59 virtual void SetPin(const std::string& pin) OVERRIDE;
24 virtual void Stop() OVERRIDE; 60 virtual void Stop() OVERRIDE;
25 61
26 private: 62 private:
63 void DoGetConfig(const GetConfigCallback& callback);
64 void DoSetConfigAndStart(scoped_ptr<base::DictionaryValue> config);
65 void DoStop();
66
67 bool RunToolScriptAsRoot(const char* command);
68 bool StopService();
69
70 // The API for gaining root privileges is blocking (it prompts the user for
71 // a password). Since Start() and Stop() must not block the main thread, they
72 // need to post their tasks to a separate thread.
73 base::Thread auth_thread_;
74
75 // This distinguishes between STATE_START_FAILED and STATE_STOPPED in
76 // GetState().
77 bool start_failed_;
78 base::Lock start_failed_lock_;
79
27 DISALLOW_COPY_AND_ASSIGN(DaemonControllerMac); 80 DISALLOW_COPY_AND_ASSIGN(DaemonControllerMac);
28 }; 81 };
29 82
30 DaemonControllerMac::DaemonControllerMac() { 83 DaemonControllerMac::DaemonControllerMac()
84 : auth_thread_("Auth thread"),
85 start_failed_(false) {
86 auth_thread_.Start();
87 }
88
89 DaemonControllerMac::~DaemonControllerMac() {
90 // This will block if the thread is waiting on a root password prompt. There
91 // doesn't seem to be an easy solution for this, other than to spawn a
92 // separate process to do the root elevation.
93
94 // TODO(lambroslambrou): Improve this, either by finding a way to terminate
95 // the thread, or by moving to a separate process.
96 auth_thread_.Stop();
31 } 97 }
32 98
33 DaemonController::State DaemonControllerMac::GetState() { 99 DaemonController::State DaemonControllerMac::GetState() {
34 return DaemonController::STATE_NOT_IMPLEMENTED; 100 pid_t job_pid = base::mac::PIDForJob(kServiceName);
101 if (job_pid < 0) {
102 // TODO(lambroslambrou): Change this to STATE_NOT_INSTALLED when the
103 // installation process is implemented.
104 return remoting::DaemonController::STATE_NOT_IMPLEMENTED;
Jamie 2012/03/29 05:20:29 Nit: You're already in namespace remoting. No need
Lambros 2012/03/29 18:20:44 Done.
105 } else if (job_pid == 0) {
106 // Service is stopped, or a start attempt failed.
107 base::AutoLock lock(start_failed_lock_);
108 return start_failed_ ? remoting::DaemonController::STATE_START_FAILED :
109 remoting::DaemonController::STATE_STOPPED;
110 } else {
111 return remoting::DaemonController::STATE_STARTED;
112 }
35 } 113 }
36 114
37 void DaemonControllerMac::GetConfig(const GetConfigCallback& callback) { 115 void DaemonControllerMac::GetConfig(const GetConfigCallback& callback) {
38 NOTIMPLEMENTED(); 116 // base::Unretained() is safe, since this object owns the thread and therefore
117 // outlives it.
118 auth_thread_.message_loop_proxy()->PostTask(
119 FROM_HERE,
120 base::Bind(&DaemonControllerMac::DoGetConfig, base::Unretained(this),
121 callback));
39 } 122 }
40 123
41 void DaemonControllerMac::SetConfigAndStart( 124 void DaemonControllerMac::SetConfigAndStart(
42 scoped_ptr<base::DictionaryValue> config) { 125 scoped_ptr<base::DictionaryValue> config) {
43 NOTIMPLEMENTED(); 126 {
127 base::AutoLock lock(start_failed_lock_);
128 start_failed_ = false;
129 }
130
131 auth_thread_.message_loop_proxy()->PostTask(
132 FROM_HERE,
133 base::Bind(&DaemonControllerMac::DoSetConfigAndStart,
134 base::Unretained(this), base::Passed(&config)));
44 } 135 }
45 136
46 void DaemonControllerMac::SetPin(const std::string& pin) { 137 void DaemonControllerMac::SetPin(const std::string& pin) {
47 NOTIMPLEMENTED(); 138 NOTIMPLEMENTED();
48 } 139 }
49 140
50 void DaemonControllerMac::Stop() { 141 void DaemonControllerMac::Stop() {
51 NOTIMPLEMENTED(); 142 auth_thread_.message_loop_proxy()->PostTask(
143 FROM_HERE,
144 base::Bind(&DaemonControllerMac::DoStop, base::Unretained(this)));
145 }
146
147 void DaemonControllerMac::DoGetConfig(const GetConfigCallback& callback) {
148 JsonHostConfig host_config(FilePath(kHostConfigFile),
149 base::MessageLoopProxy::current());
150 host_config.Read();
151
152 scoped_ptr<base::DictionaryValue> config(new base::DictionaryValue());
153
154 const char* key = "host_id";
155 std::string value;
156 if (host_config.GetString(key, &value))
157 config.get()->SetString(key, value);
158 key = "xmpp_login";
159 if (host_config.GetString(key, &value))
160 config.get()->SetString(key, value);
161
162 callback.Run(config.Pass());
163 }
164
165 void DaemonControllerMac::DoSetConfigAndStart(
166 scoped_ptr<base::DictionaryValue> config) {
167 // JsonHostConfig doesn't provide a way to save on the current thread, wait
168 // for completion, and know whether the save succeeded. Instead, use
169 // base::JSONWriter directly.
170
171 // TODO(lambroslambrou): Improve the JsonHostConfig interface.
172 std::string file_content;
173 base::JSONWriter::WriteWithOptions(config.get(),
174 base::JSONWriter::OPTIONS_PRETTY_PRINT,
Jamie 2012/03/29 05:20:29 Optional: Pretty-printing doesn't seem necessary.
Lambros 2012/03/29 18:20:44 Done.
175 &file_content);
176 if (file_util::WriteFile(FilePath(kHostConfigFile), file_content.c_str(),
177 file_content.size()) !=
178 static_cast<int>(file_content.size())) {
179 LOG(ERROR) << "Failed to write config file: " << kHostConfigFile;
180 base::AutoLock lock(start_failed_lock_);
181 start_failed_ = true;
182 return;
183 }
184
185 // Creating the trigger file causes launchd to start the service, so the
186 // extra step performed in DoStop() is not necessary here.
187 if (!RunToolScriptAsRoot("--enable")) {
188 base::AutoLock lock(start_failed_lock_);
189 start_failed_ = true;
190 }
191 }
192
193 void DaemonControllerMac::DoStop() {
194 if (!RunToolScriptAsRoot("--disable"))
195 return;
196
197 // Deleting the trigger file does not cause launchd to stop the service.
198 // Since the service is running for the local user's desktop (not as root),
199 // it has to be stopped for that user. This cannot easily be done in the
200 // shell-script running as root, so it is done here instead.
201 StopService();
202 }
203
204 bool DaemonControllerMac::RunToolScriptAsRoot(const char* command) {
205 base::mac::ScopedAuthorizationRef authorization(
206 base::mac::AuthorizationCreateToRunAsRoot(CFSTR("")));
dmac 2012/03/29 14:52:41 No prompt?
Lambros 2012/03/29 18:20:44 Added TODO.
207 if (!authorization) {
208 LOG(ERROR) << "Failed to get root privileges.";
209 return false;
210 }
211
212 const char* arguments[] = { command, NULL };
213 int exit_status;
214 OSStatus status = base::mac::ExecuteWithPrivilegesAndWait(
dmac 2012/03/29 14:52:41 Doing it this way is somewhat deprecated on Lion.
Lambros 2012/03/29 18:20:44 This is non-trivial, unfortunately. The SM framew
215 authorization.get(),
216 kStartStopTool,
dmac 2012/03/29 14:52:41 instead of running kStartStopTool directly with fu
Lambros 2012/03/29 18:20:44 Raised bug-report and added TODO for this. On 201
dmac 2012/03/29 23:46:25 OK... I would consider this to be of high priority
217 kAuthorizationFlagDefaults,
218 arguments,
219 NULL,
220 &exit_status);
221 if (status != errAuthorizationSuccess) {
222 OSSTATUS_LOG(ERROR, status) << "AuthorizationExecuteWithPrivileges";
223 return false;
224 }
225 if (exit_status != 0) {
226 LOG(ERROR) << kStartStopTool << " failed with exit status " << exit_status;
227 return false;
228 }
229
230 return true;
231 }
232
233 bool DaemonControllerMac::StopService() {
234 base::mac::ScopedLaunchData response(
235 base::mac::MessageForJob(kServiceName, LAUNCH_KEY_STOPJOB));
236 if (!response) {
237 LOG(ERROR) << "Failed to send message to launchd";
238 return false;
239 }
240
241 // Got a response, so check if launchd sent a non-zero error code, otherwise
242 // assume the command was successful.
243 if (launch_data_get_type(response.get()) == LAUNCH_DATA_ERRNO) {
244 int error = launch_data_get_errno(response.get());
245 if (error) {
246 LOG(ERROR) << "launchd returned error " << error;
247 return false;
248 }
249 }
250 return true;
52 } 251 }
53 252
54 } // namespace 253 } // namespace
55 254
56 DaemonController* remoting::DaemonController::Create() { 255 DaemonController* remoting::DaemonController::Create() {
57 return new DaemonControllerMac(); 256 return new DaemonControllerMac();
58 } 257 }
59 258
60 } // namespace remoting 259 } // namespace remoting
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698