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

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: Fix nits. Created 8 years, 8 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/"
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 DaemonController::STATE_NOT_IMPLEMENTED;
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_ ? DaemonController::STATE_START_FAILED :
109 DaemonController::STATE_STOPPED;
110 } else {
111 return 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::Write(config.get(), &file_content);
174 if (file_util::WriteFile(FilePath(kHostConfigFile), file_content.c_str(),
175 file_content.size()) !=
176 static_cast<int>(file_content.size())) {
177 LOG(ERROR) << "Failed to write config file: " << kHostConfigFile;
178 base::AutoLock lock(start_failed_lock_);
179 start_failed_ = true;
180 return;
181 }
182
183 // Creating the trigger file causes launchd to start the service, so the
184 // extra step performed in DoStop() is not necessary here.
185 if (!RunToolScriptAsRoot("--enable")) {
186 base::AutoLock lock(start_failed_lock_);
187 start_failed_ = true;
188 }
189 }
190
191 void DaemonControllerMac::DoStop() {
192 if (!RunToolScriptAsRoot("--disable"))
193 return;
194
195 // Deleting the trigger file does not cause launchd to stop the service.
196 // Since the service is running for the local user's desktop (not as root),
197 // it has to be stopped for that user. This cannot easily be done in the
198 // shell-script running as root, so it is done here instead.
199 StopService();
200 }
201
202 bool DaemonControllerMac::RunToolScriptAsRoot(const char* command) {
203 // TODO(lambroslambrou): Supply a localized prompt string here.
204 base::mac::ScopedAuthorizationRef authorization(
205 base::mac::AuthorizationCreateToRunAsRoot(CFSTR("")));
206 if (!authorization) {
207 LOG(ERROR) << "Failed to get root privileges.";
208 return false;
209 }
210
211 // TODO(lambroslambrou): Check permissions and ownership of tool script, and
212 // use sandbox-exec to minimize exposure - http://crbug.com/120903
213 const char* arguments[] = { command, NULL };
214 int exit_status;
215 OSStatus status = base::mac::ExecuteWithPrivilegesAndWait(
216 authorization.get(),
217 kStartStopTool,
218 kAuthorizationFlagDefaults,
219 arguments,
220 NULL,
221 &exit_status);
222 if (status != errAuthorizationSuccess) {
223 OSSTATUS_LOG(ERROR, status) << "AuthorizationExecuteWithPrivileges";
224 return false;
225 }
226 if (exit_status != 0) {
227 LOG(ERROR) << kStartStopTool << " failed with exit status " << exit_status;
228 return false;
229 }
230
231 return true;
232 }
233
234 bool DaemonControllerMac::StopService() {
235 base::mac::ScopedLaunchData response(
236 base::mac::MessageForJob(kServiceName, LAUNCH_KEY_STOPJOB));
237 if (!response) {
238 LOG(ERROR) << "Failed to send message to launchd";
239 return false;
240 }
241
242 // Got a response, so check if launchd sent a non-zero error code, otherwise
243 // assume the command was successful.
244 if (launch_data_get_type(response.get()) == LAUNCH_DATA_ERRNO) {
245 int error = launch_data_get_errno(response.get());
246 if (error) {
247 LOG(ERROR) << "launchd returned error " << error;
248 return false;
249 }
250 }
251 return true;
52 } 252 }
53 253
54 } // namespace 254 } // namespace
55 255
56 DaemonController* remoting::DaemonController::Create() { 256 DaemonController* remoting::DaemonController::Create() {
57 return new DaemonControllerMac(); 257 return new DaemonControllerMac();
58 } 258 }
59 259
60 } // namespace remoting 260 } // 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