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

Unified 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | remoting/remoting.gyp » ('j') | remoting/remoting.gyp » ('J')
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: remoting/host/plugin/daemon_controller_mac.cc
diff --git a/remoting/host/plugin/daemon_controller_mac.cc b/remoting/host/plugin/daemon_controller_mac.cc
index 1a8a2e0f8d6815f49146d2e012f416a00b0a0055..a130fdef4353c9da23c8e85c774d49765284fb55 100644
--- a/remoting/host/plugin/daemon_controller_mac.cc
+++ b/remoting/host/plugin/daemon_controller_mac.cc
@@ -4,17 +4,40 @@
#include "remoting/host/plugin/daemon_controller.h"
+#include <launch.h>
+#include <ServiceManagement/ServiceManagement.h>
+
#include "base/basictypes.h"
+#include "base/bind.h"
#include "base/compiler_specific.h"
#include "base/logging.h"
+#include "base/mac/authorization_util.h"
+#include "base/mac/foundation_util.h"
+#include "base/mac/mac_logging.h"
+#include "base/mac/scoped_authorizationref.h"
+#include "base/mac/scoped_cftyperef.h"
+#include "base/threading/thread.h"
namespace remoting {
namespace {
+// The name of the Remoting Host service that is registered with launchd.
+#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.
+
+// This helper script is executed as root. It is passed a command-line option
+// (--enable or --disable), which causes it to create or remove a trigger file.
+// The trigger file (defined in the service's plist file) informs launchd
+// whether the Host service should be running. Creating the trigger file causes
+// launchd to immediately start the service. Deleting the trigger file has no
+// immediate effect, but it prevents the service from being restarted if it
+// becomes stopped.
+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.
+
class DaemonControllerMac : public remoting::DaemonController {
public:
DaemonControllerMac();
+ virtual ~DaemonControllerMac();
virtual State GetState() OVERRIDE;
virtual bool SetPin(const std::string& pin) OVERRIDE;
@@ -22,14 +45,53 @@ class DaemonControllerMac : public remoting::DaemonController {
virtual bool Stop() OVERRIDE;
private:
+ void DoStart();
+ void DoStop();
+
+ bool RunToolScriptAsRoot(const char* command);
+ bool StopService();
+
+ // The API for gaining root privileges is blocking (it prompts the user for
+ // a password). Since Start() and Stop() must not block the main thread, they
+ // need to post their tasks to a separate thread.
+ 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.
+
+ // This distinguishes between STATE_START_FAILED and STATE_STOPPED in
+ // GetState().
+ bool start_failed_;
+
DISALLOW_COPY_AND_ASSIGN(DaemonControllerMac);
};
-DaemonControllerMac::DaemonControllerMac() {
+DaemonControllerMac::DaemonControllerMac()
+ : root_thread_("Root thread"),
+ start_failed_(false) {
+ root_thread_.Start();
+}
+
+DaemonControllerMac::~DaemonControllerMac() {
+ // This will block if the thread is waiting on a root password prompt. There
+ // doesn't seem to be an easy solution for this, other than to spawn a
+ // separate process to do the root elevation.
+
+ // TODO(lambroslambrou): Improve this, either by finding a way to terminate
+ // the thread, or by moving to a separate process.
+ root_thread_.Stop();
}
remoting::DaemonController::State DaemonControllerMac::GetState() {
- return remoting::DaemonController::STATE_NOT_IMPLEMENTED;
+ base::mac::ScopedCFTypeRef<CFDictionaryRef> job(
+ SMJobCopyDictionary(kSMDomainUserLaunchd, CFSTR(kServiceName)));
+ if (!job)
+ 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.
+
+ if (CFDictionaryGetValue(job.get(), CFSTR("PID"))) {
+ return remoting::DaemonController::STATE_STARTED;
+ } else {
+ // Service is stopped, or a start attempt failed.
+ return start_failed_ ? remoting::DaemonController::STATE_START_FAILED :
+ remoting::DaemonController::STATE_STOPPED;
+ }
}
bool DaemonControllerMac::SetPin(const std::string& pin) {
@@ -38,13 +100,93 @@ bool DaemonControllerMac::SetPin(const std::string& pin) {
}
bool DaemonControllerMac::Start() {
- NOTIMPLEMENTED();
- return false;
+ start_failed_ = false;
+ root_thread_.message_loop_proxy()->PostTask(
+ FROM_HERE,
+ 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.
+ return true;
}
bool DaemonControllerMac::Stop() {
- NOTIMPLEMENTED();
- return false;
+ root_thread_.message_loop_proxy()->PostTask(
+ FROM_HERE,
+ base::Bind(&DaemonControllerMac::DoStop, base::Unretained(this)));
+ return true;
+}
+
+void DaemonControllerMac::DoStart() {
+ // Creating the trigger file causes launchd to start the service, so the
+ // extra step performed in DoStop() is not necessary here.
+ if (!RunToolScriptAsRoot("--enable"))
+ start_failed_ = true;
+}
+
+void DaemonControllerMac::DoStop() {
+ if (!RunToolScriptAsRoot("--disable"))
+ return;
+
+ // Deleting the trigger file does not cause launchd to stop the service.
+ // Since the service is running for the local user's desktop (not as root),
+ // it has to be stopped for that user. This cannot easily be done in the
+ // shell-script running as root, so it is done here instead.
+ StopService();
+}
+
+bool DaemonControllerMac::RunToolScriptAsRoot(const char* command) {
+ base::mac::ScopedAuthorizationRef authorization(
+ base::mac::AuthorizationCreateToRunAsRoot(CFSTR("")));
+ if (!authorization) {
+ LOG(ERROR) << "Failed to get root privileges.";
+ return false;
+ }
+
+ 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.
+ int exit_status;
+ OSStatus status = base::mac::ExecuteWithPrivilegesAndWait(
+ authorization.get(),
+ kStartStopTool,
+ kAuthorizationFlagDefaults,
+ arguments,
+ NULL,
+ &exit_status);
+ if (status != errAuthorizationSuccess) {
+ OSSTATUS_LOG(ERROR, status) << "AuthorizationExecuteWithPrivileges";
+ return false;
+ }
+ if (exit_status != 0) {
+ LOG(ERROR) << kStartStopTool << " failed with exit status " << exit_status;
+ return false;
+ }
+
+ return true;
+}
+
+bool DaemonControllerMac::StopService() {
+ 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
+ launch_data_dict_insert(message, launch_data_new_string(kServiceName),
+ LAUNCH_KEY_STOPJOB);
+ launch_data_t response = launch_msg(message);
+ launch_data_free(message);
+
+ if (!response) {
+ LOG(ERROR) << "Failed to send message to launchd";
+ return false;
+ }
+
+ bool success = false;
+ if (launch_data_get_type(response) == LAUNCH_DATA_ERRNO) {
+ int error = launch_data_get_errno(response);
+ if (error) {
+ LOG(ERROR) << "launchd returned error " << error;
+ } else {
+ success = true;
+ }
+ } else {
+ LOG(ERROR) << "launchd returned unexpected response";
+ }
+
+ launch_data_free(response);
+ return success;
}
} // namespace
« 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