diff options
Diffstat (limited to 'remoting/host/plugin/daemon_controller_mac.cc')
-rw-r--r-- | remoting/host/plugin/daemon_controller_mac.cc | 190 |
1 files changed, 63 insertions, 127 deletions
diff --git a/remoting/host/plugin/daemon_controller_mac.cc b/remoting/host/plugin/daemon_controller_mac.cc index a75d613..a59c164 100644 --- a/remoting/host/plugin/daemon_controller_mac.cc +++ b/remoting/host/plugin/daemon_controller_mac.cc @@ -10,15 +10,14 @@ #include "base/basictypes.h" #include "base/bind.h" #include "base/compiler_specific.h" -#include "base/eintr_wrapper.h" #include "base/file_path.h" #include "base/file_util.h" #include "base/json/json_writer.h" #include "base/logging.h" -#include "base/mac/authorization_util.h" +#include "base/mac/foundation_util.h" #include "base/mac/launchd.h" #include "base/mac/mac_logging.h" -#include "base/mac/scoped_authorizationref.h" +#include "base/mac/mac_util.h" #include "base/mac/scoped_launch_data.h" #include "base/threading/thread.h" #include "base/time.h" @@ -29,6 +28,12 @@ namespace remoting { namespace { +// The NSSystemDirectories.h header has a conflicting definition of +// NSSearchPathDirectory with the one in base/mac/foundation_util.h. +// Foundation.h would work, but it can only be included from Objective-C files. +// Therefore, we define the needed constants here. +const int NSLibraryDirectory = 5; + // The name of the Remoting Host service that is registered with launchd. #define kServiceName "org.chromium.chromoting" #define kConfigDir "/Library/PrivilegedHelperTools/" @@ -72,13 +77,8 @@ class DaemonControllerMac : public remoting::DaemonController { void NotifyWhenStopped(const CompletionCallback& done_callback, int tries_remaining, const base::TimeDelta& sleep); + bool ShowPreferencePane(const std::string& config_data); - bool RunToolScriptAsRoot(const char* command, const std::string& input_data); - bool SendJobControlMessage(const char* launchKey); - - // 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 auth_thread_; DISALLOW_COPY_AND_ASSIGN(DaemonControllerMac); @@ -90,12 +90,6 @@ DaemonControllerMac::DaemonControllerMac() } 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. auth_thread_.Stop(); } @@ -167,13 +161,11 @@ void DaemonControllerMac::DoGetConfig(const GetConfigCallback& callback) { void DaemonControllerMac::DoSetConfigAndStart( scoped_ptr<base::DictionaryValue> config, const CompletionCallback& done_callback) { - std::string file_content; - base::JSONWriter::Write(config.get(), &file_content); - if (!RunToolScriptAsRoot("--enable", file_content)) { - done_callback.Run(RESULT_FAILED); - return; - } - bool result = SendJobControlMessage(LAUNCH_KEY_STARTJOB); + std::string config_data; + base::JSONWriter::Write(config.get(), &config_data); + + bool result = ShowPreferencePane(config_data); + done_callback.Run(result ? RESULT_OK : RESULT_FAILED); } @@ -197,36 +189,65 @@ void DaemonControllerMac::DoUpdateConfig( config_file.SetString(*key, value); } - std::string file_content = config_file.GetSerializedData(); - if (!RunToolScriptAsRoot("--save-config", file_content)) { - done_callback.Run(RESULT_FAILED); - return; + std::string config_data = config_file.GetSerializedData(); + bool success = ShowPreferencePane(config_data); + + done_callback.Run(success ? RESULT_OK : RESULT_FAILED); +} + +bool DaemonControllerMac::ShowPreferencePane(const std::string& config_data) { + if (!config_data.empty()) { + FilePath config_path; + if (!file_util::GetTempDir(&config_path)) { + LOG(ERROR) << "Failed to get filename for saving configuration data."; + return false; + } + config_path = config_path.Append(kServiceName ".json"); + + int written = file_util::WriteFile(config_path, config_data.data(), + config_data.size()); + if (written != static_cast<int>(config_data.size())) { + LOG(ERROR) << "Failed to save configuration data to: " + << config_path.value(); + return false; + } } - done_callback.Run(RESULT_OK); - pid_t job_pid = base::mac::PIDForJob(kServiceName); - if (job_pid > 0) { - kill(job_pid, SIGHUP); + FilePath pane_path; + // TODO(lambroslambrou): Use NSPreferencePanesDirectory once we start + // building against SDK 10.6. + if (!base::mac::GetLocalDirectory(NSLibraryDirectory, &pane_path)) { + LOG(ERROR) << "Failed to get directory for local preference panes."; + return false; } -} + pane_path = pane_path.Append("PreferencePanes") + .Append(kServiceName ".prefPane"); -void DaemonControllerMac::DoStop(const CompletionCallback& done_callback) { - if (!RunToolScriptAsRoot("--disable", "")) { - done_callback.Run(RESULT_FAILED); - return; + FSRef pane_path_ref; + if (!base::mac::FSRefFromPath(pane_path.value(), &pane_path_ref)) { + LOG(ERROR) << "Failed to create FSRef"; + return false; + } + OSStatus status = LSOpenFSRef(&pane_path_ref, NULL); + if (status != noErr) { + OSSTATUS_LOG(ERROR, status) << "LSOpenFSRef failed for path: " + << pane_path.value(); + return false; } - // 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. - if (!SendJobControlMessage(LAUNCH_KEY_STOPJOB)) { + CFNotificationCenterRef center = + CFNotificationCenterGetDistributedCenter(); + CFNotificationCenterPostNotification(center, CFSTR(kServiceName), NULL, NULL, + TRUE); + return true; +} + +void DaemonControllerMac::DoStop(const CompletionCallback& done_callback) { + if (!ShowPreferencePane("")) { done_callback.Run(RESULT_FAILED); return; } - // SendJobControlMessage does not wait for the stop to take effect, so we - // can't return immediately. Instead, we wait up to 10s. NotifyWhenStopped(done_callback, kStopWaitRetryLimit, base::TimeDelta::FromMilliseconds(kStopWaitTimeout)); @@ -252,91 +273,6 @@ void DaemonControllerMac::NotifyWhenStopped( } } -bool DaemonControllerMac::RunToolScriptAsRoot(const char* command, - const std::string& input_data) { - // TODO(lambroslambrou): Supply a localized prompt string here. - base::mac::ScopedAuthorizationRef authorization( - base::mac::AuthorizationCreateToRunAsRoot(CFSTR(""))); - if (!authorization) { - LOG(ERROR) << "Failed to get root privileges."; - return false; - } - - if (!file_util::VerifyPathControlledByAdmin(FilePath(kStartStopTool))) { - LOG(ERROR) << "Security check failed for: " << kStartStopTool; - return false; - } - - // TODO(lambroslambrou): Use sandbox-exec to minimize exposure - - // http://crbug.com/120903 - const char* arguments[] = { command, NULL }; - FILE* pipe = NULL; - pid_t pid; - OSStatus status = base::mac::ExecuteWithPrivilegesAndGetPID( - authorization.get(), - kStartStopTool, - kAuthorizationFlagDefaults, - arguments, - &pipe, - &pid); - if (status != errAuthorizationSuccess) { - OSSTATUS_LOG(ERROR, status) << "AuthorizationExecuteWithPrivileges"; - return false; - } - if (pid == -1) { - LOG(ERROR) << "Failed to get child PID"; - return false; - } - - DCHECK(pipe); - if (!input_data.empty()) { - size_t bytes_written = fwrite(input_data.data(), sizeof(char), - input_data.size(), pipe); - // According to the fwrite manpage, a partial count is returned only if a - // write error has occurred. - if (bytes_written != input_data.size()) { - LOG(ERROR) << "Failed to write data to child process"; - } - // Need to close, since the child waits for EOF on its stdin. - if (fclose(pipe) != 0) { - PLOG(ERROR) << "fclose"; - } - } - - int exit_status; - pid_t wait_result = HANDLE_EINTR(waitpid(pid, &exit_status, 0)); - if (wait_result != pid) { - PLOG(ERROR) << "waitpid"; - return false; - } - if (WIFEXITED(exit_status) && WEXITSTATUS(exit_status) == 0) { - return true; - } else { - LOG(ERROR) << kStartStopTool << " failed with exit status " << exit_status; - return false; - } -} - -bool DaemonControllerMac::SendJobControlMessage(const char* launchKey) { - base::mac::ScopedLaunchData response( - base::mac::MessageForJob(kServiceName, launchKey)); - if (!response) { - LOG(ERROR) << "Failed to send message to launchd"; - return false; - } - - // Got a response, so check if launchd sent a non-zero error code, otherwise - // assume the command was successful. - if (launch_data_get_type(response.get()) == LAUNCH_DATA_ERRNO) { - int error = launch_data_get_errno(response.get()); - if (error) { - LOG(ERROR) << "launchd returned error " << error; - return false; - } - } - return true; -} - } // namespace scoped_ptr<DaemonController> remoting::DaemonController::Create() { |