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

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: 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 | remoting/remoting.gyp » ('j') | remoting/remoting.gyp » ('J')
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 <ServiceManagement/ServiceManagement.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"
9 #include "base/logging.h" 13 #include "base/logging.h"
14 #include "base/mac/authorization_util.h"
15 #include "base/mac/foundation_util.h"
16 #include "base/mac/mac_logging.h"
17 #include "base/mac/scoped_authorizationref.h"
18 #include "base/mac/scoped_cftyperef.h"
19 #include "base/threading/thread.h"
10 20
11 namespace remoting { 21 namespace remoting {
12 22
13 namespace { 23 namespace {
14 24
25 // The name of the Remoting Host service that is registered with launchd.
26 #define kServiceName "com.google.chrome_remote_desktop"
Jamie 2012/03/23 03:24:05 Any reason not to make this a const char[]?
Lambros 2012/03/26 19:15:26 The CFSTR() macro seems to need a literal string.
27
28 // This helper script is executed as root. It is passed a command-line option
29 // (--enable or --disable), which causes it to create or remove a trigger file.
30 // The trigger file (defined in the service's plist file) informs launchd
31 // whether the Host service should be running. Creating the trigger file causes
32 // launchd to immediately start the service. Deleting the trigger file has no
33 // immediate effect, but it prevents the service from being restarted if it
34 // becomes stopped.
35 const char kStartStopTool[] = "/Library/PrivilegedHelperTools/me2me.sh";
garykac 2012/03/23 16:28:29 Since this is a system directory that includes fil
Lambros 2012/03/26 19:15:26 Done.
36
15 class DaemonControllerMac : public remoting::DaemonController { 37 class DaemonControllerMac : public remoting::DaemonController {
16 public: 38 public:
17 DaemonControllerMac(); 39 DaemonControllerMac();
40 virtual ~DaemonControllerMac();
18 41
19 virtual State GetState() OVERRIDE; 42 virtual State GetState() OVERRIDE;
20 virtual bool SetPin(const std::string& pin) OVERRIDE; 43 virtual bool SetPin(const std::string& pin) OVERRIDE;
21 virtual bool Start() OVERRIDE; 44 virtual bool Start() OVERRIDE;
22 virtual bool Stop() OVERRIDE; 45 virtual bool Stop() OVERRIDE;
23 46
24 private: 47 private:
48 void DoStart();
49 void DoStop();
50
51 bool RunToolScriptAsRoot(const char* command);
52 bool StopService();
53
54 // The API for gaining root privileges is blocking (it prompts the user for
55 // a password). Since Start() and Stop() must not block the main thread, they
56 // need to post their tasks to a separate thread.
57 base::Thread root_thread_;
Jamie 2012/03/23 03:24:05 I think auth_thread might be a better name, since
Sergey Ulanov 2012/03/23 17:56:48 +1
Lambros 2012/03/26 19:15:26 Done.
58
59 // This distinguishes between STATE_START_FAILED and STATE_STOPPED in
60 // GetState().
61 bool start_failed_;
62
25 DISALLOW_COPY_AND_ASSIGN(DaemonControllerMac); 63 DISALLOW_COPY_AND_ASSIGN(DaemonControllerMac);
26 }; 64 };
27 65
28 DaemonControllerMac::DaemonControllerMac() { 66 DaemonControllerMac::DaemonControllerMac()
67 : root_thread_("Root thread"),
68 start_failed_(false) {
69 root_thread_.Start();
70 }
71
72 DaemonControllerMac::~DaemonControllerMac() {
73 // This will block if the thread is waiting on a root password prompt. There
74 // doesn't seem to be an easy solution for this, other than to spawn a
75 // separate process to do the root elevation.
76
77 // TODO(lambroslambrou): Improve this, either by finding a way to terminate
78 // the thread, or by moving to a separate process.
79 root_thread_.Stop();
29 } 80 }
30 81
31 remoting::DaemonController::State DaemonControllerMac::GetState() { 82 remoting::DaemonController::State DaemonControllerMac::GetState() {
32 return remoting::DaemonController::STATE_NOT_IMPLEMENTED; 83 base::mac::ScopedCFTypeRef<CFDictionaryRef> job(
84 SMJobCopyDictionary(kSMDomainUserLaunchd, CFSTR(kServiceName)));
85 if (!job)
86 return remoting::DaemonController::STATE_NOT_INSTALLED;
Jamie 2012/03/23 03:24:05 Until we have an installation story, I think you s
Lambros 2012/03/26 19:15:26 Done.
87
88 if (CFDictionaryGetValue(job.get(), CFSTR("PID"))) {
89 return remoting::DaemonController::STATE_STARTED;
90 } else {
91 // Service is stopped, or a start attempt failed.
92 return start_failed_ ? remoting::DaemonController::STATE_START_FAILED :
93 remoting::DaemonController::STATE_STOPPED;
94 }
33 } 95 }
34 96
35 bool DaemonControllerMac::SetPin(const std::string& pin) { 97 bool DaemonControllerMac::SetPin(const std::string& pin) {
36 NOTIMPLEMENTED(); 98 NOTIMPLEMENTED();
37 return false; 99 return false;
38 } 100 }
39 101
40 bool DaemonControllerMac::Start() { 102 bool DaemonControllerMac::Start() {
41 NOTIMPLEMENTED(); 103 start_failed_ = false;
42 return false; 104 root_thread_.message_loop_proxy()->PostTask(
105 FROM_HERE,
106 base::Bind(&DaemonControllerMac::DoStart, base::Unretained(this)));
Sergey Ulanov 2012/03/23 17:56:48 Add comment that it is safe to use base::Unretaine
Lambros 2012/03/26 19:15:26 Done.
107 return true;
43 } 108 }
44 109
45 bool DaemonControllerMac::Stop() { 110 bool DaemonControllerMac::Stop() {
46 NOTIMPLEMENTED(); 111 root_thread_.message_loop_proxy()->PostTask(
47 return false; 112 FROM_HERE,
113 base::Bind(&DaemonControllerMac::DoStop, base::Unretained(this)));
114 return true;
115 }
116
117 void DaemonControllerMac::DoStart() {
118 // Creating the trigger file causes launchd to start the service, so the
119 // extra step performed in DoStop() is not necessary here.
120 if (!RunToolScriptAsRoot("--enable"))
121 start_failed_ = true;
122 }
123
124 void DaemonControllerMac::DoStop() {
125 if (!RunToolScriptAsRoot("--disable"))
126 return;
127
128 // Deleting the trigger file does not cause launchd to stop the service.
129 // Since the service is running for the local user's desktop (not as root),
130 // it has to be stopped for that user. This cannot easily be done in the
131 // shell-script running as root, so it is done here instead.
132 StopService();
133 }
134
135 bool DaemonControllerMac::RunToolScriptAsRoot(const char* command) {
136 base::mac::ScopedAuthorizationRef authorization(
137 base::mac::AuthorizationCreateToRunAsRoot(CFSTR("")));
138 if (!authorization) {
139 LOG(ERROR) << "Failed to get root privileges.";
140 return false;
141 }
142
143 const char* arguments[] = {command, NULL};
Sergey Ulanov 2012/03/23 17:56:48 nit: add spaces after { and before }.
Lambros 2012/03/26 19:15:26 Done.
144 int exit_status;
145 OSStatus status = base::mac::ExecuteWithPrivilegesAndWait(
146 authorization.get(),
147 kStartStopTool,
148 kAuthorizationFlagDefaults,
149 arguments,
150 NULL,
151 &exit_status);
152 if (status != errAuthorizationSuccess) {
153 OSSTATUS_LOG(ERROR, status) << "AuthorizationExecuteWithPrivileges";
154 return false;
155 }
156 if (exit_status != 0) {
157 LOG(ERROR) << kStartStopTool << " failed with exit status " << exit_status;
158 return false;
159 }
160
161 return true;
162 }
163
164 bool DaemonControllerMac::StopService() {
165 launch_data_t message = launch_data_alloc(LAUNCH_DATA_DICTIONARY);
Sergey Ulanov 2012/03/23 17:56:48 Maybe move ScopedLaunchData from chrome/browser/ma
166 launch_data_dict_insert(message, launch_data_new_string(kServiceName),
167 LAUNCH_KEY_STOPJOB);
168 launch_data_t response = launch_msg(message);
169 launch_data_free(message);
170
171 if (!response) {
172 LOG(ERROR) << "Failed to send message to launchd";
173 return false;
174 }
175
176 bool success = false;
177 if (launch_data_get_type(response) == LAUNCH_DATA_ERRNO) {
178 int error = launch_data_get_errno(response);
179 if (error) {
180 LOG(ERROR) << "launchd returned error " << error;
181 } else {
182 success = true;
183 }
184 } else {
185 LOG(ERROR) << "launchd returned unexpected response";
186 }
187
188 launch_data_free(response);
189 return success;
48 } 190 }
49 191
50 } // namespace 192 } // namespace
51 193
52 DaemonController* remoting::DaemonController::Create() { 194 DaemonController* remoting::DaemonController::Create() {
53 return new DaemonControllerMac(); 195 return new DaemonControllerMac();
54 } 196 }
55 197
56 } // namespace remoting 198 } // namespace remoting
OLDNEW
« no previous file with comments | « no previous file | remoting/remoting.gyp » ('j') | remoting/remoting.gyp » ('J')

Powered by Google App Engine
This is Rietveld 408576698