diff options
author | dmaclach@chromium.org <dmaclach@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-24 17:14:36 +0000 |
---|---|---|
committer | dmaclach@chromium.org <dmaclach@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-24 17:14:36 +0000 |
commit | 56f0f264ff866052ebcb24e75147cb600e6547a1 (patch) | |
tree | 8b16bded93f914cacbf3cb130fae3539ad4bb268 | |
parent | 04a8454da64b62bfad5091efd5cf1143443283f0 (diff) | |
download | chromium_src-56f0f264ff866052ebcb24e75147cb600e6547a1.zip chromium_src-56f0f264ff866052ebcb24e75147cb600e6547a1.tar.gz chromium_src-56f0f264ff866052ebcb24e75147cb600e6547a1.tar.bz2 |
Get service process running standalone on the mac by hooking it into launchd.
BUG=NONE
TEST=BUILD
Review URL: http://codereview.chromium.org/6482016
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@75893 0039d316-1c4b-4281-b951-d872f2087c98
24 files changed, 738 insertions, 289 deletions
@@ -250,7 +250,7 @@ deps_os = { "/trunk/deps/reference_builds/chrome_mac@71120", "src/third_party/GTM": - (Var("googlecode_url") % "google-toolbox-for-mac") + "/trunk@427", + (Var("googlecode_url") % "google-toolbox-for-mac") + "/trunk@434", "src/third_party/pdfsqueeze": (Var("googlecode_url") % "pdfsqueeze") + "/trunk@4", "src/third_party/lighttpd": diff --git a/base/process_util.h b/base/process_util.h index a7f8496..29b08ca 100644 --- a/base/process_util.h +++ b/base/process_util.h @@ -162,9 +162,6 @@ void CloseProcessHandle(ProcessHandle process); ProcessId GetProcId(ProcessHandle process); #if defined(OS_LINUX) -// Returns the ID for the parent of the given process. -ProcessId GetParentProcessId(ProcessHandle process); - // Returns the path to the executable of the given process. FilePath GetProcessExecutablePath(ProcessHandle process); @@ -182,6 +179,9 @@ bool AdjustOOMScore(ProcessId process, int score); #endif #if defined(OS_POSIX) +// Returns the ID for the parent of the given process. +ProcessId GetParentProcessId(ProcessHandle process); + // Close all file descriptors, except those which are a destination in the // given multimap. Only call this function in a child process where you know // that there aren't any other threads. @@ -359,7 +359,7 @@ bool KillProcessById(ProcessId process_id, int exit_code, bool wait); // will no longer be available). TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code); -// Waits for process to exit. In POSIX systems, if the process hasn't been +// Waits for process to exit. On POSIX systems, if the process hasn't been // signaled then puts the exit code in |exit_code|; otherwise it's considered // a failure. On Windows |exit_code| is always filled. Returns true on success, // and closes |handle| in any case. @@ -382,9 +382,9 @@ bool WaitForProcessesToExit(const FilePath::StringType& executable_name, const ProcessFilter* filter); // Wait for a single process to exit. Return true if it exited cleanly within -// the given time limit. -bool WaitForSingleProcess(ProcessHandle handle, - int64 wait_milliseconds); +// the given time limit. On Linux |handle| must be a child process, however +// on Mac and Windows it can be any process. +bool WaitForSingleProcess(ProcessHandle handle, int64 wait_milliseconds); // Returns true when |wait_milliseconds| have elapsed and the process // is still running. diff --git a/base/process_util_mac.mm b/base/process_util_mac.mm index aa0f14d..6d05581 100644 --- a/base/process_util_mac.mm +++ b/base/process_util_mac.mm @@ -880,4 +880,17 @@ void EnableTerminationOnOutOfMemory() { reinterpret_cast<IMP>(oom_killer_allocWithZone)); } +ProcessId GetParentProcessId(ProcessHandle process) { + struct kinfo_proc info; + size_t length = sizeof(struct kinfo_proc); + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, process }; + if (sysctl(mib, 4, &info, &length, NULL, 0) < 0) { + PLOG(ERROR) << "sysctl"; + return -1; + } + if (length == 0) + return -1; + return info.kp_eproc.e_ppid; +} + } // namespace base diff --git a/base/process_util_posix.cc b/base/process_util_posix.cc index 9d0ba58..9a5f5a7 100644 --- a/base/process_util_posix.cc +++ b/base/process_util_posix.cc @@ -32,6 +32,7 @@ #if defined(OS_MACOSX) #include <crt_externs.h> +#include <sys/event.h> #define environ (*_NSGetEnviron()) #else extern char** environ; @@ -496,17 +497,20 @@ bool LaunchAppImpl( scoped_array<char*> new_environ(AlterEnvironment(env_changes, environ)); pid = fork(); - if (pid < 0) + if (pid < 0) { + PLOG(ERROR) << "fork"; return false; - + } if (pid == 0) { // Child process if (start_new_process_group) { // Instead of inheriting the process group ID of the parent, the child // starts off a new process group with pgid equal to its process ID. - if (setpgid(0, 0) < 0) + if (setpgid(0, 0) < 0) { + PLOG(ERROR) << "setpgid"; return false; + } } #if defined(OS_MACOSX) RestoreDefaultExceptionHandler(); @@ -713,7 +717,79 @@ bool WaitForExitCodeWithTimeout(ProcessHandle handle, int* exit_code, return true; } +#if defined(OS_MACOSX) +// Using kqueue on Mac so that we can wait on non-child processes. +// We can't use kqueues on child processes because we need to reap +// our own children using wait. +static bool WaitForSingleNonChildProcess(ProcessHandle handle, + int64 wait_milliseconds) { + int kq = kqueue(); + if (kq == -1) { + PLOG(ERROR) << "kqueue"; + return false; + } + + struct kevent change = { 0 }; + EV_SET(&change, handle, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL); + + struct timespec spec; + struct timespec *spec_ptr; + if (wait_milliseconds != base::kNoTimeout) { + time_t sec = static_cast<time_t>(wait_milliseconds / 1000); + wait_milliseconds = wait_milliseconds - (sec * 1000); + spec.tv_sec = sec; + spec.tv_nsec = wait_milliseconds * 1000000L; + spec_ptr = &spec; + } else { + spec_ptr = NULL; + } + + while(true) { + struct kevent event = { 0 }; + int event_count = HANDLE_EINTR(kevent(kq, &change, 1, &event, 1, spec_ptr)); + if (close(kq) != 0) { + PLOG(ERROR) << "close"; + } + if (event_count < 0) { + PLOG(ERROR) << "kevent"; + return false; + } else if (event_count == 0) { + if (wait_milliseconds != base::kNoTimeout) { + // Timed out. + return false; + } + } else if ((event_count == 1) && + (handle == static_cast<pid_t>(event.ident)) && + (event.filter == EVFILT_PROC)) { + if (event.fflags == NOTE_EXIT) { + return true; + } else if (event.flags == EV_ERROR) { + LOG(ERROR) << "kevent error " << event.data; + return false; + } else { + NOTREACHED(); + return false; + } + } else { + NOTREACHED(); + return false; + } + } +} +#endif // OS_MACOSX + bool WaitForSingleProcess(ProcessHandle handle, int64 wait_milliseconds) { + ProcessHandle parent_pid = GetParentProcessId(handle); + ProcessHandle our_pid = Process::Current().handle(); + if (parent_pid != our_pid) { +#if defined(OS_MACOSX) + // On Mac we can wait on non child processes. + return WaitForSingleNonChildProcess(handle, wait_milliseconds); +#else + // Currently on Linux we can't handle non child processes. + NOTIMPLEMENTED(); +#endif // OS_MACOSX + } bool waitpid_success; int status; if (wait_milliseconds == base::kNoTimeout) diff --git a/base/process_util_unittest.cc b/base/process_util_unittest.cc index 1b7368a..31e1dec 100644 --- a/base/process_util_unittest.cc +++ b/base/process_util_unittest.cc @@ -658,12 +658,12 @@ TEST_F(ProcessUtilTest, GetAppOutputRestrictedNoZombies) { } } -#if defined(OS_LINUX) TEST_F(ProcessUtilTest, GetParentProcessId) { base::ProcessId ppid = base::GetParentProcessId(base::GetCurrentProcId()); EXPECT_EQ(ppid, getppid()); } +#if defined(OS_LINUX) TEST_F(ProcessUtilTest, ParseProcStatCPU) { // /proc/self/stat for a process running "top". const char kTopStat[] = "960 (top) S 16230 960 16230 34818 960 " @@ -683,7 +683,7 @@ TEST_F(ProcessUtilTest, ParseProcStatCPU) { EXPECT_EQ(0, base::ParseProcStatCPU(kSelfStat)); } -#endif +#endif // defined(OS_LINUX) #endif // defined(OS_POSIX) diff --git a/chrome/browser/service/service_process_control.cc b/chrome/browser/service/service_process_control.cc index 09bc1ad..eb7a983 100644 --- a/chrome/browser/service/service_process_control.cc +++ b/chrome/browser/service/service_process_control.cc @@ -21,79 +21,6 @@ #include "chrome/common/service_process_util.h" #include "ui/base/ui_base_switches.h" -// ServiceProcessControl::Launcher implementation. -// This class is responsible for launching the service process on the -// PROCESS_LAUNCHER thread. -class ServiceProcessControl::Launcher - : public base::RefCountedThreadSafe<ServiceProcessControl::Launcher> { - public: - Launcher(ServiceProcessControl* process, CommandLine* cmd_line) - : process_(process), - cmd_line_(cmd_line), - launched_(false), - retry_count_(0) { - } - - // Execute the command line to start the process asynchronously. - // After the comamnd is executed |task| is called with the process handle on - // the UI thread. - void Run(Task* task) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - - notify_task_.reset(task); - BrowserThread::PostTask(BrowserThread::PROCESS_LAUNCHER, FROM_HERE, - NewRunnableMethod(this, &Launcher::DoRun)); - } - - bool launched() const { return launched_; } - - private: - void DoRun() { - DCHECK(notify_task_.get()); - base::LaunchApp(*cmd_line_.get(), false, true, NULL); - BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - NewRunnableMethod(this, &Launcher::DoDetectLaunched)); - } - - void DoDetectLaunched() { - DCHECK(notify_task_.get()); - const uint32 kMaxLaunchDetectRetries = 10; - - { - // We should not be doing blocking disk IO from this thread! - // Temporarily allowed until we fix - // http://code.google.com/p/chromium/issues/detail?id=60207 - base::ThreadRestrictions::ScopedAllowIO allow_io; - launched_ = CheckServiceProcessReady(); - } - - if (launched_ || (retry_count_ >= kMaxLaunchDetectRetries)) { - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, - NewRunnableMethod(this, &Launcher::Notify)); - return; - } - retry_count_++; - // If the service process is not launched yet then check again in 2 seconds. - const int kDetectLaunchRetry = 2000; - MessageLoop::current()->PostDelayedTask( - FROM_HERE, - NewRunnableMethod(this, &Launcher::DoDetectLaunched), - kDetectLaunchRetry); - } - - void Notify() { - DCHECK(notify_task_.get()); - notify_task_->Run(); - notify_task_.reset(); - } - - ServiceProcessControl* process_; - scoped_ptr<CommandLine> cmd_line_; - scoped_ptr<Task> notify_task_; - bool launched_; - uint32 retry_count_; -}; // ServiceProcessControl implementation. ServiceProcessControl::ServiceProcessControl(Profile* profile) @@ -120,7 +47,7 @@ void ServiceProcessControl::ConnectInternal() { base::Thread* io_thread = g_browser_process->io_thread(); // TODO(hclam): Handle error connecting to channel. - const std::string channel_id = GetServiceProcessChannelName(); + const IPC::ChannelHandle channel_id = GetServiceProcessChannel(); channel_.reset( new IPC::SyncChannel(channel_id, IPC::Channel::MODE_NAMED_CLIENT, this, io_thread->message_loop(), true, @@ -201,10 +128,19 @@ void ServiceProcessControl::Launch(Task* success_task, Task* failure_task) { if (!logging_level.empty()) cmd_line->AppendSwitchASCII(switches::kLoggingLevel, logging_level); + std::string v_level = browser_command_line.GetSwitchValueASCII( + switches::kV); + if (!v_level.empty()) + cmd_line->AppendSwitchASCII(switches::kV, v_level); + if (browser_command_line.HasSwitch(switches::kWaitForDebuggerChildren)) { cmd_line->AppendSwitch(switches::kWaitForDebugger); } + if (browser_command_line.HasSwitch(switches::kEnableLogging)) { + cmd_line->AppendSwitch(switches::kEnableLogging); + } + std::string locale = g_browser_process->GetApplicationLocale(); cmd_line->AppendSwitchASCII(switches::kLang, locale); @@ -341,3 +277,56 @@ void ServiceProcessControl::RemoveMessageHandler( } DISABLE_RUNNABLE_METHOD_REFCOUNT(ServiceProcessControl); + +ServiceProcessControl::Launcher::Launcher(ServiceProcessControl* process, + CommandLine* cmd_line) + : process_(process), + cmd_line_(cmd_line), + launched_(false), + retry_count_(0) { +} + +// Execute the command line to start the process asynchronously. +// After the command is executed, |task| is called with the process handle on +// the UI thread. +void ServiceProcessControl::Launcher::Run(Task* task) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + notify_task_.reset(task); + BrowserThread::PostTask(BrowserThread::PROCESS_LAUNCHER, FROM_HERE, + NewRunnableMethod(this, &Launcher::DoRun)); +} + +void ServiceProcessControl::Launcher::Notify() { + DCHECK(notify_task_.get()); + notify_task_->Run(); + notify_task_.reset(); +} + +#if !defined(OS_MACOSX) +void ServiceProcessControl::Launcher::DoDetectLaunched() { + DCHECK(notify_task_.get()); + const uint32 kMaxLaunchDetectRetries = 10; + launched_ = CheckServiceProcessReady(); + if (launched_ || (retry_count_ >= kMaxLaunchDetectRetries)) { + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + NewRunnableMethod(this, &Launcher::Notify)); + return; + } + retry_count_++; + + // If the service process is not launched yet then check again in 2 seconds. + const int kDetectLaunchRetry = 2000; + MessageLoop::current()->PostDelayedTask( + FROM_HERE, + NewRunnableMethod(this, &Launcher::DoDetectLaunched), + kDetectLaunchRetry); +} + +void ServiceProcessControl::Launcher::DoRun() { + DCHECK(notify_task_.get()); + base::LaunchApp(*cmd_line_.get(), false, true, NULL); + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + NewRunnableMethod(this, &Launcher::DoDetectLaunched)); +} +#endif // !OS_MACOSX diff --git a/chrome/browser/service/service_process_control.h b/chrome/browser/service/service_process_control.h index c2f18ba..d5e43e0 100644 --- a/chrome/browser/service/service_process_control.h +++ b/chrome/browser/service/service_process_control.h @@ -10,6 +10,7 @@ #include <string> #include <vector> +#include "base/basictypes.h" #include "base/id_map.h" #include "base/callback.h" #include "base/process.h" @@ -20,6 +21,7 @@ #include "ipc/ipc_sync_channel.h" class Profile; +class CommandLine; namespace remoting { struct ChromotingHostInfo; @@ -126,7 +128,33 @@ class ServiceProcessControl : public IPC::Channel::Sender, void RemoveMessageHandler(MessageHandler* message_handler); private: - class Launcher; + // This class is responsible for launching the service process on the + // PROCESS_LAUNCHER thread. + class Launcher + : public base::RefCountedThreadSafe<ServiceProcessControl::Launcher> { + public: + Launcher(ServiceProcessControl* process, CommandLine* cmd_line); + // Execute the command line to start the process asynchronously. + // After the comamnd is executed |task| is called with the process handle on + // the UI thread. + void Run(Task* task); + + bool launched() const { return launched_; } + + private: +#if !defined(OS_MACOSX) + void DoDetectLaunched(); +#endif // !OS_MACOSX + + void DoRun(); + void Notify(); + ServiceProcessControl* process_; + scoped_ptr<CommandLine> cmd_line_; + scoped_ptr<Task> notify_task_; + bool launched_; + uint32 retry_count_; + }; + typedef std::vector<Task*> TaskList; // Helper method to invoke all the callbacks based on success on failure. diff --git a/chrome/browser/service/service_process_control_browsertest.cc b/chrome/browser/service/service_process_control_browsertest.cc index a0dee5d..ff6e514 100644 --- a/chrome/browser/service/service_process_control_browsertest.cc +++ b/chrome/browser/service/service_process_control_browsertest.cc @@ -25,6 +25,13 @@ class ServiceProcessControlBrowserTest ServiceProcessControlManager::GetInstance()->Shutdown(); } +#if defined(OS_MACOSX) + virtual void TearDown() { + // ForceServiceProcessShutdown removes the process from launchd on Mac. + ForceServiceProcessShutdown("", 0); + } +#endif // OS_MACOSX + protected: void LaunchServiceProcessControl() { ServiceProcessControl* process = @@ -72,7 +79,7 @@ class ServiceProcessControlBrowserTest void ProcessControlLaunched() { base::ProcessId service_pid; - EXPECT_TRUE(GetServiceProcessSharedData(NULL, &service_pid)); + EXPECT_TRUE(GetServiceProcessData(NULL, &service_pid)); EXPECT_NE(static_cast<base::ProcessId>(0), service_pid); EXPECT_TRUE(base::OpenProcessHandleWithAccess( service_pid, @@ -194,7 +201,7 @@ IN_PROC_BROWSER_TEST_F(ServiceProcessControlBrowserTest, // Make sure we are connected to the service process. EXPECT_TRUE(process()->is_connected()); base::ProcessId service_pid; - EXPECT_TRUE(GetServiceProcessSharedData(NULL, &service_pid)); + EXPECT_TRUE(GetServiceProcessData(NULL, &service_pid)); EXPECT_NE(static_cast<base::ProcessId>(0), service_pid); chrome::VersionInfo version_info; ForceServiceProcessShutdown(version_info.Version(), service_pid); @@ -203,10 +210,10 @@ IN_PROC_BROWSER_TEST_F(ServiceProcessControlBrowserTest, IN_PROC_BROWSER_TEST_F(ServiceProcessControlBrowserTest, CheckPid) { base::ProcessId service_pid; - EXPECT_FALSE(GetServiceProcessSharedData(NULL, &service_pid)); + EXPECT_FALSE(GetServiceProcessData(NULL, &service_pid)); // Launch the service process. LaunchServiceProcessControl(); - EXPECT_TRUE(GetServiceProcessSharedData(NULL, &service_pid)); + EXPECT_TRUE(GetServiceProcessData(NULL, &service_pid)); EXPECT_NE(static_cast<base::ProcessId>(0), service_pid); } diff --git a/chrome/browser/service/service_process_control_mac.mm b/chrome/browser/service/service_process_control_mac.mm new file mode 100644 index 0000000..ebb2f2d --- /dev/null +++ b/chrome/browser/service/service_process_control_mac.mm @@ -0,0 +1,25 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/service/service_process_control.h" + +#include "base/command_line.h" +#include "base/mac/scoped_cftyperef.h" +#include "chrome/browser/browser_thread.h" +#include "chrome/common/service_process_util_posix.h" +#include "third_party/GTM/Foundation/GTMServiceManagement.h" + +void ServiceProcessControl::Launcher::DoRun() { + base::mac::ScopedCFTypeRef<CFDictionaryRef> launchd_plist( + CreateServiceProcessLaunchdPlist(cmd_line_.get())); + CFErrorRef error = NULL; + if (!GTMSMJobSubmit(launchd_plist, &error)) { + LOG(ERROR) << error; + CFRelease(error); + } else { + launched_ = true; + } + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, + NewRunnableMethod(this, &Launcher::Notify)); +} diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index b290923..6db074f 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -1860,6 +1860,7 @@ 'browser/search_engines/util.cc', 'browser/search_engines/util.h', 'browser/service/service_process_control.cc', + 'browser/service/service_process_control_mac.mm', 'browser/service/service_process_control.h', 'browser/service/service_process_control_manager.cc', 'browser/service/service_process_control_manager.h', @@ -3697,6 +3698,8 @@ '../third_party/GTM/Foundation/GTMNSNumber+64Bit.m', '../third_party/GTM/Foundation/GTMNSObject+KeyValueObserving.h', '../third_party/GTM/Foundation/GTMNSObject+KeyValueObserving.m', + '../third_party/GTM/Foundation/GTMServiceManagement.h', + '../third_party/GTM/Foundation/GTMServiceManagement.c', # MolokoCacao additions '../third_party/molokocacao/NSBezierPath+MCAdditions.h', '../third_party/molokocacao/NSBezierPath+MCAdditions.m', diff --git a/chrome/chrome_common.gypi b/chrome/chrome_common.gypi index dbdc1c8..37f6cc8 100644 --- a/chrome/chrome_common.gypi +++ b/chrome/chrome_common.gypi @@ -363,7 +363,10 @@ 'common/service_messages_internal.h', 'common/service_process_util.cc', 'common/service_process_util.h', + 'common/service_process_util_linux.cc', + 'common/service_process_util_mac.mm', 'common/service_process_util_posix.cc', + 'common/service_process_util_posix.h', 'common/service_process_util_win.cc', 'common/socket_stream_dispatcher.cc', 'common/socket_stream_dispatcher.h', @@ -488,6 +491,9 @@ 'common/common.sb', ], }, + 'include_dirs': [ + '../third_party/GTM', + ], }], ['OS!="win"', { 'sources!': [ diff --git a/chrome/common/service_process_util.cc b/chrome/common/service_process_util.cc index 458f662..6a2f02f 100644 --- a/chrome/common/service_process_util.cc +++ b/chrome/common/service_process_util.cc @@ -21,6 +21,8 @@ #include "chrome/common/chrome_version_info.h" #include "chrome/common/service_process_util.h" +#if !defined(OS_MACOSX) + namespace { // This should be more than enough to hold a version string assuming each part @@ -49,7 +51,7 @@ enum ServiceProcessRunningState { ServiceProcessRunningState GetServiceProcessRunningState( std::string* service_version_out, base::ProcessId* pid_out) { std::string version; - if (!GetServiceProcessSharedData(&version, pid_out)) + if (!GetServiceProcessData(&version, pid_out)) return SERVICE_NOT_RUNNING; #if defined(OS_POSIX) @@ -125,14 +127,9 @@ std::string GetServiceProcessScopedVersionedName( return GetServiceProcessScopedName(versioned_str); } -// Gets the name of the service process IPC channel. -std::string GetServiceProcessChannelName() { - return GetServiceProcessScopedVersionedName("_service_ipc"); -} - // Reads the named shared memory to get the shared data. Returns false if no // matching shared memory was found. -bool GetServiceProcessSharedData(std::string* version, base::ProcessId* pid) { +bool GetServiceProcessData(std::string* version, base::ProcessId* pid) { scoped_ptr<base::SharedMemory> shared_mem_service_data; shared_mem_service_data.reset(new base::SharedMemory()); ServiceProcessSharedData* service_data = NULL; @@ -153,17 +150,22 @@ bool GetServiceProcessSharedData(std::string* version, base::ProcessId* pid) { return false; } +// Gets the name of the service process IPC channel. +IPC::ChannelHandle GetServiceProcessChannel() { + return GetServiceProcessScopedVersionedName("_service_ipc"); +} + +#endif // !OS_MACOSX + ServiceProcessState::ServiceProcessState() : state_(NULL) { } ServiceProcessState::~ServiceProcessState() { +#if !defined(OS_MACOSX) if (shared_mem_service_data_.get()) { - // Delete needs a pool wrapped around it because it calls some Obj-C on Mac, - // and since ServiceProcessState is a singleton, it gets destructed after - // the standard NSAutoreleasePools have already been cleaned up. - base::mac::ScopedNSAutoreleasePool pool; shared_mem_service_data_->Delete(GetServiceProcessSharedMemName()); } +#endif // !OS_MACOSX TearDownState(); } @@ -172,7 +174,16 @@ ServiceProcessState* ServiceProcessState::GetInstance() { return Singleton<ServiceProcessState>::get(); } +void ServiceProcessState::SignalStopped() { + TearDownState(); + shared_mem_service_data_.reset(); +} + +#if !defined(OS_MACOSX) bool ServiceProcessState::Initialize() { + if (!InitializeState()) { + return false; + } if (!TakeSingletonLock()) { return false; } @@ -242,11 +253,8 @@ bool ServiceProcessState::CreateSharedData() { return true; } -std::string ServiceProcessState::GetAutoRunKey() { - return GetServiceProcessScopedName("_service_run"); +IPC::ChannelHandle ServiceProcessState::GetServiceProcessChannel() { + return ::GetServiceProcessChannel(); } -void ServiceProcessState::SignalStopped() { - TearDownState(); - shared_mem_service_data_.reset(); -} +#endif // !OS_MACOSX diff --git a/chrome/common/service_process_util.h b/chrome/common/service_process_util.h index 175499d..2ad72d7 100644 --- a/chrome/common/service_process_util.h +++ b/chrome/common/service_process_util.h @@ -7,11 +7,14 @@ #include <string> +#include "base/basictypes.h" #include "base/process.h" #include "base/scoped_ptr.h" #include "base/shared_memory.h" +#include "ipc/ipc_channel_handle.h" class Task; +class CommandLine; namespace base { class MessageLoopProxy; @@ -20,9 +23,9 @@ namespace base { template <typename T> struct DefaultSingletonTraits; // Return the IPC channel to connect to the service process. -// -std::string GetServiceProcessChannelName(); +IPC::ChannelHandle GetServiceProcessChannel(); +#if !defined(OS_MACOSX) // Return a name that is scoped to this instance of the service process. We // use the user-data-dir as a scoping prefix. std::string GetServiceProcessScopedName(const std::string& append_str); @@ -30,6 +33,7 @@ std::string GetServiceProcessScopedName(const std::string& append_str); // Return a name that is scoped to this instance of the service process. We // use the user-data-dir and the version as a scoping prefix. std::string GetServiceProcessScopedVersionedName(const std::string& append_str); +#endif // OS_MACOSX // The following methods are used in a process that acts as a client to the // service process (typically the browser process). @@ -43,7 +47,7 @@ bool CheckServiceProcessReady(); // a true return value only means that some process shared data was available, // and not that the process is ready to receive IPC commands, or even running. // This method is only exposed for testing. -bool GetServiceProcessSharedData(std::string* version, base::ProcessId* pid); +bool GetServiceProcessData(std::string* version, base::ProcessId* pid); // -------------------------------------------------------------------------- // Forces a service process matching the specified version to shut down. @@ -76,15 +80,19 @@ class ServiceProcessState { void SignalStopped(); // Register the service process to run on startup. - bool AddToAutoRun(); + bool AddToAutoRun(CommandLine* command_line); // Unregister the service process to run on startup. bool RemoveFromAutoRun(); + // Return the channel handle used for communicating with the service. + IPC::ChannelHandle GetServiceProcessChannel(); + private: ServiceProcessState(); ~ServiceProcessState(); +#if !defined(OS_MACOSX) // Create the shared memory data for the service process. bool CreateSharedData(); @@ -95,9 +103,10 @@ class ServiceProcessState { // Acquires a singleton lock for the service process. A return value of false // means that a service process instance is already running. bool TakeSingletonLock(); +#endif // !OS_MACOSX - // Key used to register the service process to auto-run. - std::string GetAutoRunKey(); + // Initialize the platform specific state. + bool InitializeState(); // Tear down the platform specific state. void TearDownState(); diff --git a/chrome/common/service_process_util_linux.cc b/chrome/common/service_process_util_linux.cc new file mode 100644 index 0000000..1a53889 --- /dev/null +++ b/chrome/common/service_process_util_linux.cc @@ -0,0 +1,79 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/service_process_util_posix.h" + +#include <signal.h> +#include <unistd.h> + +#include "base/file_util.h" +#include "base/logging.h" +#include "base/threading/platform_thread.h" +#include "chrome/common/multi_process_lock.h" + +namespace { + +// Attempts to take a lock named |name|. If |waiting| is true then this will +// make multiple attempts to acquire the lock. +// Caller is responsible for ownership of the MultiProcessLock. +MultiProcessLock* TakeNamedLock(const std::string& name, bool waiting) { + scoped_ptr<MultiProcessLock> lock(MultiProcessLock::Create(name)); + if (lock == NULL) return NULL; + bool got_lock = false; + for (int i = 0; i < 10; ++i) { + if (lock->TryLock()) { + got_lock = true; + break; + } + if (!waiting) break; + base::PlatformThread::Sleep(100 * i); + } + if (!got_lock) { + lock.reset(); + } + return lock.release(); +} + +MultiProcessLock* TakeServiceInitializingLock(bool waiting) { + std::string lock_name = + GetServiceProcessScopedName("_service_initializing"); + return TakeNamedLock(lock_name, waiting); +} + +} // namespace + +MultiProcessLock* TakeServiceRunningLock(bool waiting) { + std::string lock_name = + GetServiceProcessScopedName("_service_running"); + return TakeNamedLock(lock_name, waiting); +} + +bool ForceServiceProcessShutdown(const std::string& version, + base::ProcessId process_id) { + if (kill(process_id, SIGTERM) < 0) { + PLOG(ERROR) << "kill"; + return false; + } + return true; +} + +bool CheckServiceProcessReady() { + scoped_ptr<MultiProcessLock> running_lock(TakeServiceRunningLock(false)); + return running_lock.get() == NULL; +} + +bool ServiceProcessState::TakeSingletonLock() { + state_->initializing_lock_.reset(TakeServiceInitializingLock(true)); + return state_->initializing_lock_.get(); +} + +bool ServiceProcessState::AddToAutoRun(CommandLine* cmd_line) { + NOTIMPLEMENTED(); + return false; +} + +bool ServiceProcessState::RemoveFromAutoRun() { + NOTIMPLEMENTED(); + return false; +} diff --git a/chrome/common/service_process_util_mac.mm b/chrome/common/service_process_util_mac.mm new file mode 100644 index 0000000..632538a --- /dev/null +++ b/chrome/common/service_process_util_mac.mm @@ -0,0 +1,228 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/service_process_util_posix.h" + +#import <Foundation/Foundation.h> +#include <launch.h> + +#include "base/command_line.h" +#include "base/file_path.h" +#include "base/mac/foundation_util.h" +#include "base/mac/mac_util.h" +#include "base/mac/scoped_nsautorelease_pool.h" +#include "base/path_service.h" +#include "base/scoped_nsobject.h" +#include "base/sys_string_conversions.h" +#include "base/version.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/chrome_version_info.h" +#include "third_party/GTM/Foundation/GTMServiceManagement.h" + +namespace { + +NSString* GetServiceProcessLaunchDLabel() { + NSString *bundle_id = [base::mac::MainAppBundle() bundleIdentifier]; + NSString *label = [bundle_id stringByAppendingString:@".service_process"]; + FilePath user_data_dir; + PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); + std::string user_data_dir_path = user_data_dir.value(); + NSString *ns_path = base::SysUTF8ToNSString(user_data_dir_path); + ns_path = [ns_path stringByReplacingOccurrencesOfString:@" " + withString:@"_"]; + label = [label stringByAppendingString:ns_path]; + return label; +} + +NSString* GetServiceProcessLaunchDSocketKey() { + return @"ServiceProcessSocket"; +} + +NSString* GetServiceProcessLaunchDSocketEnvVar() { + NSString *label = GetServiceProcessLaunchDLabel(); + NSString *env_var = [label stringByReplacingOccurrencesOfString:@"." + withString:@"_"]; + env_var = [env_var stringByAppendingString:@"_SOCKET"]; + env_var = [env_var uppercaseString]; + return env_var; +} + +} + +// Gets the name of the service process IPC channel. +IPC::ChannelHandle GetServiceProcessChannel() { + std::string socket_path; + scoped_nsobject<NSDictionary> dictionary( + base::mac::CFToNSCast(GTMCopyLaunchdExports())); + NSString *ns_socket_path = + [dictionary objectForKey:GetServiceProcessLaunchDSocketEnvVar()]; + if (ns_socket_path) { + socket_path = base::SysNSStringToUTF8(ns_socket_path); + } + return IPC::ChannelHandle(socket_path); +} + +bool ForceServiceProcessShutdown(const std::string& /* version */, + base::ProcessId /* process_id */) { + NSString* label = GetServiceProcessLaunchDLabel(); + CFErrorRef err = NULL; + bool ret = GTMSMJobRemove(reinterpret_cast<CFStringRef>(label), &err); + if (!ret) { + LOG(ERROR) << "ForceServiceProcessShutdown: " << err; + CFRelease(err); + } + return ret; +} + +bool GetServiceProcessData(std::string* version, base::ProcessId* pid) { + CFStringRef label = + reinterpret_cast<CFStringRef>(GetServiceProcessLaunchDLabel()); + scoped_nsobject<NSDictionary> launchd_conf( + base::mac::CFToNSCast(GTMSMJobCopyDictionary(label))); + if (!launchd_conf.get()) { + return false; + } + + if (version) { + NSString *exe_path = [launchd_conf objectForKey:@ LAUNCH_JOBKEY_PROGRAM]; + if (!exe_path) { + NOTREACHED() << "Failed to get exe path"; + return false; + } + NSString *bundle_path = [[[exe_path stringByDeletingLastPathComponent] + stringByDeletingLastPathComponent] + stringByDeletingLastPathComponent]; + NSBundle *bundle = [NSBundle bundleWithPath:bundle_path]; + if (!bundle) { + NOTREACHED() << "Unable to get bundle at: " + << reinterpret_cast<CFStringRef>(bundle_path); + return false; + } + NSString *ns_version = + [bundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"]; + if (!ns_version) { + NOTREACHED() << "Unable to get version at: " + << reinterpret_cast<CFStringRef>(bundle_path); + return false; + } + *version = base::SysNSStringToUTF8(ns_version); + } + if (pid) { + NSNumber* ns_pid = [launchd_conf objectForKey:@ LAUNCH_JOBKEY_PID]; + if (ns_pid) { + *pid = [ns_pid intValue]; + } else { + *pid = 0; + } + } + return true; +} + +bool ServiceProcessState::Initialize() { + if (!InitializeState()) { + return false; + } + CFErrorRef err = NULL; + state_->launchd_conf_.reset(GTMSMJobCheckIn(&err)); + if (!state_->launchd_conf_.get()) { + LOG(ERROR) << "InitializePlatformState: " << err; + CFRelease(err); + return false; + } + return true; +} + +IPC::ChannelHandle ServiceProcessState::GetServiceProcessChannel() { + CHECK(state_); + NSDictionary *ns_launchd_conf = base::mac::CFToNSCast(state_->launchd_conf_); + NSDictionary* socket_dict = + [ns_launchd_conf objectForKey:@ LAUNCH_JOBKEY_SOCKETS]; + NSArray* sockets = + [socket_dict objectForKey:GetServiceProcessLaunchDSocketKey()]; + CHECK_EQ([sockets count], 1U); + int socket = [[sockets objectAtIndex:0] intValue]; + base::FileDescriptor fd(socket, false); + return IPC::ChannelHandle(std::string(), fd); +} + +bool CheckServiceProcessReady() { + std::string version; + pid_t pid; + if (!GetServiceProcessData(&version, &pid)) { + return false; + } + scoped_ptr<Version> service_version(Version::GetVersionFromString(version)); + bool ready = true; + if (!service_version.get()) { + ready = false; + } else { + chrome::VersionInfo version_info; + if (!version_info.is_valid()) { + // Our own version is invalid. This is an error case. Pretend that we + // are out of date. + NOTREACHED() << "Failed to get current file version"; + ready = true; + } + else { + scoped_ptr<Version> running_version(Version::GetVersionFromString( + version_info.Version())); + if (!running_version.get()) { + // Our own version is invalid. This is an error case. Pretend that we + // are out of date. + NOTREACHED() << "Failed to parse version info"; + ready = true; + } else if (running_version->CompareTo(*service_version) > 0) { + ready = false; + } else { + ready = true; + } + } + } + if (!ready) { + ForceServiceProcessShutdown(version, pid); + } + return ready; +} + +bool ServiceProcessState::AddToAutoRun(CommandLine* cmd_line) { + NOTIMPLEMENTED(); + return false; +} + +bool ServiceProcessState::RemoveFromAutoRun() { + NOTIMPLEMENTED(); + return false; +} + +CFDictionaryRef CreateServiceProcessLaunchdPlist(CommandLine* cmd_line) { + base::mac::ScopedNSAutoreleasePool pool; + + NSString *program = + base::SysUTF8ToNSString(cmd_line->GetProgram().value()); + + std::vector<std::string> args = cmd_line->argv(); + NSMutableArray *ns_args = [NSMutableArray arrayWithCapacity:args.size()]; + + for (std::vector<std::string>::iterator iter = args.begin(); + iter < args.end(); + ++iter) { + [ns_args addObject:base::SysUTF8ToNSString(*iter)]; + } + + NSDictionary *socket = + [NSDictionary dictionaryWithObject:GetServiceProcessLaunchDSocketEnvVar() + forKey:@ LAUNCH_JOBSOCKETKEY_SECUREWITHKEY]; + NSDictionary *sockets = + [NSDictionary dictionaryWithObject:socket + forKey:GetServiceProcessLaunchDSocketKey()]; + + NSDictionary *launchd_plist = + [[NSDictionary alloc] initWithObjectsAndKeys: + GetServiceProcessLaunchDLabel(), @ LAUNCH_JOBKEY_LABEL, + program, @ LAUNCH_JOBKEY_PROGRAM, + ns_args, @ LAUNCH_JOBKEY_PROGRAMARGUMENTS, + sockets, @ LAUNCH_JOBKEY_SOCKETS, + nil]; + return reinterpret_cast<CFDictionaryRef>(launchd_plist); +} diff --git a/chrome/common/service_process_util_posix.cc b/chrome/common/service_process_util_posix.cc index 7deebaa..88c6db3 100644 --- a/chrome/common/service_process_util_posix.cc +++ b/chrome/common/service_process_util_posix.cc @@ -2,93 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "chrome/common/service_process_util.h" +#include "chrome/common/service_process_util_posix.h" -#include <signal.h> -#include <unistd.h> - -#include "base/file_util.h" -#include "base/logging.h" -#include "base/message_loop.h" +#include "base/basictypes.h" #include "base/message_loop_proxy.h" -#include "base/message_pump_libevent.h" -#include "base/path_service.h" -#include "chrome/common/chrome_paths.h" -#include "chrome/common/chrome_version_info.h" -#include "chrome/common/multi_process_lock.h" namespace { - int g_signal_socket = -1; - -// Gets the name of the lock file for service process. -FilePath GetServiceProcessLockFilePath() { - FilePath user_data_dir; - PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); - chrome::VersionInfo version_info; - std::string lock_file_name = version_info.Version() + "Service Process Lock"; - return user_data_dir.Append(lock_file_name); -} - -// Attempts to take a lock named |name|. If |waiting| is true then this will -// make multiple attempts to acquire the lock. -// Caller is responsible for ownership of the MultiProcessLock. -MultiProcessLock* TakeNamedLock(const std::string& name, bool waiting) { - scoped_ptr<MultiProcessLock> lock(MultiProcessLock::Create(name)); - if (lock == NULL) return NULL; - bool got_lock = false; - for (int i = 0; i < 10; ++i) { - if (lock->TryLock()) { - got_lock = true; - break; - } - if (!waiting) break; - base::PlatformThread::Sleep(100 * i); - } - if (!got_lock) { - lock.reset(); - } - return lock.release(); -} - -MultiProcessLock* TakeServiceRunningLock(bool waiting) { - std::string lock_name = - GetServiceProcessScopedName("_service_running"); - return TakeNamedLock(lock_name, waiting); -} - -MultiProcessLock* TakeServiceInitializingLock(bool waiting) { - std::string lock_name = - GetServiceProcessScopedName("_service_initializing"); - return TakeNamedLock(lock_name, waiting); } -} // namespace - -// Watches for |kShutDownMessage| to be written to the file descriptor it is -// watching. When it reads |kShutDownMessage|, it performs |shutdown_task_|. -// Used here to monitor the socket listening to g_signal_socket. -class ServiceProcessShutdownMonitor - : public base::MessagePumpLibevent::Watcher { - public: - - enum { - kShutDownMessage = 0xdecea5e - }; - - explicit ServiceProcessShutdownMonitor(Task* shutdown_task) - : shutdown_task_(shutdown_task) { - } - - virtual ~ServiceProcessShutdownMonitor(); - - virtual void OnFileCanReadWithoutBlocking(int fd); - virtual void OnFileCanWriteWithoutBlocking(int fd); - - private: - scoped_ptr<Task> shutdown_task_; -}; - ServiceProcessShutdownMonitor::~ServiceProcessShutdownMonitor() { } @@ -125,66 +47,39 @@ static void SigTermHandler(int sig, siginfo_t* info, void* uap) { } } -// See comment for SigTermHandler. -bool ForceServiceProcessShutdown(const std::string& version, - base::ProcessId process_id) { - if (kill(process_id, SIGTERM) < 0) { - PLOG(ERROR) << "kill"; - return false; +void ServiceProcessState::StateData::SignalReady() { + CHECK(MessageLoopForIO::current()->WatchFileDescriptor( + sockets_[0], true, MessageLoopForIO::WATCH_READ, + &watcher_, shut_down_monitor_.get())); + g_signal_socket = sockets_[1]; + + // Set up signal handler for SIGTERM. + struct sigaction action; + action.sa_sigaction = SigTermHandler; + sigemptyset(&action.sa_mask); + action.sa_flags = SA_SIGINFO; + if (sigaction(SIGTERM, &action, &old_action_) == 0) { + // If the old_action is not default, somebody else has installed a + // a competing handler. Our handler is going to override it so it + // won't be called. If this occurs it needs to be fixed. + DCHECK_EQ(old_action_.sa_handler, SIG_DFL); + set_action_ = true; +#if defined(OS_LINUX) + initializing_lock_.reset(); +#endif // OS_LINUX + } else { + PLOG(ERROR) << "sigaction"; } - return true; } -bool CheckServiceProcessReady() { - scoped_ptr<MultiProcessLock> running_lock(TakeServiceRunningLock(false)); - return running_lock.get() == NULL; -} - -struct ServiceProcessState::StateData - : public base::RefCountedThreadSafe<ServiceProcessState::StateData> { - scoped_ptr<MultiProcessLock> initializing_lock_; - scoped_ptr<MultiProcessLock> running_lock_; - scoped_ptr<ServiceProcessShutdownMonitor> shut_down_monitor_; - base::MessagePumpLibevent::FileDescriptorWatcher watcher_; - int sockets_[2]; - struct sigaction old_action_; - bool set_action_; - - // WatchFileDescriptor needs to be set up by the thread that is going - // to be monitoring it. - void SignalReady() { - CHECK(MessageLoopForIO::current()->WatchFileDescriptor( - sockets_[0], true, MessageLoopForIO::WATCH_READ, - &watcher_, shut_down_monitor_.get())); - g_signal_socket = sockets_[1]; - - // Set up signal handler for SIGTERM. - struct sigaction action; - action.sa_sigaction = SigTermHandler; - sigemptyset(&action.sa_mask); - action.sa_flags = SA_SIGINFO; - if (sigaction(SIGTERM, &action, &old_action_) == 0) { - // If the old_action is not default, somebody else has installed a - // a competing handler. Our handler is going to override it so it - // won't be called. If this occurs it needs to be fixed. - DCHECK_EQ(old_action_.sa_handler, SIG_DFL); - set_action_ = true; - initializing_lock_.reset(); - } else { - PLOG(ERROR) << "sigaction"; - } - } -}; - -bool ServiceProcessState::TakeSingletonLock() { +bool ServiceProcessState::InitializeState() { CHECK(!state_); state_ = new StateData; state_->AddRef(); state_->sockets_[0] = -1; state_->sockets_[1] = -1; state_->set_action_ = false; - state_->initializing_lock_.reset(TakeServiceInitializingLock(true)); - return state_->initializing_lock_.get(); + return true; } bool ServiceProcessState::SignalReady( @@ -192,10 +87,12 @@ bool ServiceProcessState::SignalReady( CHECK(state_); CHECK_EQ(g_signal_socket, -1); +#if defined(OS_LINUX) state_->running_lock_.reset(TakeServiceRunningLock(true)); if (state_->running_lock_.get() == NULL) { return false; } +#endif // OS_LINUX state_->shut_down_monitor_.reset( new ServiceProcessShutdownMonitor(shutdown_task)); if (pipe(state_->sockets_) < 0) { @@ -207,16 +104,6 @@ bool ServiceProcessState::SignalReady( return true; } -bool ServiceProcessState::AddToAutoRun() { - NOTIMPLEMENTED(); - return false; -} - -bool ServiceProcessState::RemoveFromAutoRun() { - NOTIMPLEMENTED(); - return false; -} - void ServiceProcessState::TearDownState() { g_signal_socket = -1; if (state_) { diff --git a/chrome/common/service_process_util_posix.h b/chrome/common/service_process_util_posix.h new file mode 100644 index 0000000..3263fb5 --- /dev/null +++ b/chrome/common/service_process_util_posix.h @@ -0,0 +1,72 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_SERVICE_PROCESS_UTIL_POSIX_H_ +#define CHROME_COMMON_SERVICE_PROCESS_UTIL_POSIX_H_ + +#include "service_process_util.h" + +#include <signal.h> + +#include "base/basictypes.h" +#include "base/message_loop.h" +#include "base/message_pump_libevent.h" +#include "base/scoped_ptr.h" + +#if defined(OS_LINUX) +#include "chrome/common/multi_process_lock.h" +MultiProcessLock* TakeServiceRunningLock(bool waiting); +#endif // OS_LINUX + +#if defined(OS_MACOSX) +#include "base/mac/scoped_cftyperef.h" +class CommandLine; +CFDictionaryRef CreateServiceProcessLaunchdPlist(CommandLine* cmd_line); +#endif // OS_MACOSX + +// Watches for |kShutDownMessage| to be written to the file descriptor it is +// watching. When it reads |kShutDownMessage|, it performs |shutdown_task_|. +// Used here to monitor the socket listening to g_signal_socket. +class ServiceProcessShutdownMonitor + : public base::MessagePumpLibevent::Watcher { + public: + + enum { + kShutDownMessage = 0xdecea5e + }; + + explicit ServiceProcessShutdownMonitor(Task* shutdown_task) + : shutdown_task_(shutdown_task) { + } + virtual ~ServiceProcessShutdownMonitor(); + + // base::MessagePumpLibevent::Watcher overrides + virtual void OnFileCanReadWithoutBlocking(int fd); + virtual void OnFileCanWriteWithoutBlocking(int fd); + + private: + scoped_ptr<Task> shutdown_task_; +}; + +struct ServiceProcessState::StateData + : public base::RefCountedThreadSafe<ServiceProcessState::StateData> { +#if defined(OS_MACOSX) + base::mac::ScopedCFTypeRef<CFDictionaryRef> launchd_conf_; +#endif // OS_MACOSX +#if defined(OS_LINUX) + scoped_ptr<MultiProcessLock> initializing_lock_; + scoped_ptr<MultiProcessLock> running_lock_; +#endif // OS_LINUX + scoped_ptr<ServiceProcessShutdownMonitor> shut_down_monitor_; + base::MessagePumpLibevent::FileDescriptorWatcher watcher_; + int sockets_[2]; + struct sigaction old_action_; + bool set_action_; + + // WatchFileDescriptor needs to be set up by the thread that is going + // to be monitoring it. + void SignalReady(); +}; + +#endif // CHROME_COMMON_SERVICE_PROCESS_UTIL_POSIX_H_ diff --git a/chrome/common/service_process_util_unittest.cc b/chrome/common/service_process_util_unittest.cc index ad6b257..eadf51f 100644 --- a/chrome/common/service_process_util_unittest.cc +++ b/chrome/common/service_process_util_unittest.cc @@ -2,6 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/basictypes.h" + +#if !defined(OS_MACOSX) +// TODO(dmaclach): Figure out tests that will work with launchd on Mac OS. + #include "base/at_exit.h" #include "base/process_util.h" #include "base/string_util.h" @@ -95,11 +100,11 @@ TEST_F(ServiceProcessStateTest, SharedMem) { // GetServiceProcessPid to lie. On Windows, we use a named event so we // don't have this issue. Until we have a more stable shared memory // implementation on Posix, this check will only execute on Windows. - ASSERT_FALSE(GetServiceProcessSharedData(&version, &pid)); + ASSERT_FALSE(GetServiceProcessData(&version, &pid)); #endif // defined(OS_WIN) ServiceProcessState* state = ServiceProcessState::GetInstance(); ASSERT_TRUE(state->Initialize()); - ASSERT_TRUE(GetServiceProcessSharedData(&version, &pid)); + ASSERT_TRUE(GetServiceProcessData(&version, &pid)); ASSERT_EQ(base::GetCurrentProcId(), pid); } @@ -113,7 +118,7 @@ TEST_F(ServiceProcessStateTest, ForceShutdown) { ASSERT_TRUE(CheckServiceProcessReady()); std::string version; base::ProcessId pid; - ASSERT_TRUE(GetServiceProcessSharedData(&version, &pid)); + ASSERT_TRUE(GetServiceProcessData(&version, &pid)); ASSERT_TRUE(ForceServiceProcessShutdown(version, pid)); int exit_code = 0; ASSERT_TRUE(base::WaitForExitCodeWithTimeout(handle, @@ -157,3 +162,4 @@ MULTIPROCESS_TEST_MAIN(ServiceProcessStateTestShutdown) { return 0; } +#endif // !OS_MACOSX diff --git a/chrome/common/service_process_util_win.cc b/chrome/common/service_process_util_win.cc index f9a1816..b617988 100644 --- a/chrome/common/service_process_util_win.cc +++ b/chrome/common/service_process_util_win.cc @@ -28,6 +28,10 @@ string16 GetServiceProcessShutdownEventName() { GetServiceProcessScopedVersionedName("_service_shutdown_evt")); } +std::string GetServiceProcessAutoRunKey() { + return GetServiceProcessScopedName("_service_run"); +} + class ServiceProcessShutdownMonitor : public base::win::ObjectWatcher::Delegate { public: @@ -85,8 +89,14 @@ struct ServiceProcessState::StateData { scoped_ptr<ServiceProcessShutdownMonitor> shutdown_monitor; }; -bool ServiceProcessState::TakeSingletonLock() { +bool ServiceProcessState::InitializeState() { DCHECK(!state_); + state_ = new StateData; + return true; +} + +bool ServiceProcessState::TakeSingletonLock() { + DCHECK(state_); string16 event_name = GetServiceProcessReadyEventName(); CHECK(event_name.length() <= MAX_PATH); base::win::ScopedHandle service_process_ready_event; @@ -96,7 +106,6 @@ bool ServiceProcessState::TakeSingletonLock() { if ((error == ERROR_ALREADY_EXISTS) || (error == ERROR_ACCESS_DENIED)) return false; DCHECK(service_process_ready_event.IsValid()); - state_ = new StateData; state_->ready_event.Set(service_process_ready_event.Take()); return true; } @@ -116,25 +125,16 @@ bool ServiceProcessState::SignalReady( return true; } -bool ServiceProcessState::AddToAutoRun() { - FilePath chrome_path; - if (PathService::Get(base::FILE_EXE, &chrome_path)) { - CommandLine cmd_line(chrome_path); - cmd_line.AppendSwitchASCII(switches::kProcessType, - switches::kServiceProcess); - // We need a unique name for the command per user-date-dir. Just use the - // channel name. - return base::win::AddCommandToAutoRun( - HKEY_CURRENT_USER, - UTF8ToWide(GetAutoRunKey()), - cmd_line.command_line_string()); - } - return false; +bool ServiceProcessState::AddToAutoRun(CommandLine* cmd_line) { + return base::win::AddCommandToAutoRun( + HKEY_CURRENT_USER, + UTF8ToWide(GetServiceProcessAutoRunKey()), + cmd_line->command_line_string()); } bool ServiceProcessState::RemoveFromAutoRun() { return base::win::RemoveCommandFromAutoRun( - HKEY_CURRENT_USER, UTF8ToWide(GetAutoRunKey())); + HKEY_CURRENT_USER, UTF8ToWide(GetServiceProcessAutoRunKey())); } void ServiceProcessState::TearDownState() { diff --git a/chrome/service/service_ipc_server.cc b/chrome/service/service_ipc_server.cc index 5e503c8..5257eec 100644 --- a/chrome/service/service_ipc_server.cc +++ b/chrome/service/service_ipc_server.cc @@ -9,8 +9,8 @@ #include "chrome/service/service_process.h" #include "ipc/ipc_logging.h" -ServiceIPCServer::ServiceIPCServer(const std::string& channel_name) - : channel_name_(channel_name), client_connected_(false) { +ServiceIPCServer::ServiceIPCServer(const IPC::ChannelHandle& channel_handle) + : channel_handle_(channel_handle), client_connected_(false) { } bool ServiceIPCServer::Init() { @@ -24,7 +24,7 @@ bool ServiceIPCServer::Init() { } void ServiceIPCServer::CreateChannel() { - channel_.reset(new IPC::SyncChannel(channel_name_, + channel_.reset(new IPC::SyncChannel(channel_handle_, IPC::Channel::MODE_NAMED_SERVER, this, g_service_process->io_thread()->message_loop(), true, g_service_process->shutdown_event())); @@ -63,8 +63,16 @@ void ServiceIPCServer::OnChannelError() { client_connected_ = false; // TODO(sanjeevr): Instead of invoking the service process for such handlers, // define a Client interface that the ServiceProcess can implement. - if (client_was_connected && g_service_process->HandleClientDisconnect()) { - CreateChannel(); + if (client_was_connected) { + if (g_service_process->HandleClientDisconnect()) { + CreateChannel(); + } + } else { + // If the client was never even connected we had an error connecting. + if (!client_connected_) { + LOG(ERROR) << "Unable to open service ipc channel " + << "named: " << channel_handle_.name; + } } } diff --git a/chrome/service/service_ipc_server.h b/chrome/service/service_ipc_server.h index 675135e..0777ec8 100644 --- a/chrome/service/service_ipc_server.h +++ b/chrome/service/service_ipc_server.h @@ -8,6 +8,7 @@ #include <string> #include "base/scoped_ptr.h" +#include "ipc/ipc_channel_handle.h" #include "ipc/ipc_sync_channel.h" #include "ipc/ipc_sync_message_filter.h" #include "ipc/ipc_message.h" @@ -16,7 +17,7 @@ class ServiceIPCServer : public IPC::Channel::Listener, public IPC::Message::Sender { public: - explicit ServiceIPCServer(const std::string& channel_name); + explicit ServiceIPCServer(const IPC::ChannelHandle& handle); virtual ~ServiceIPCServer(); bool Init(); @@ -65,7 +66,7 @@ class ServiceIPCServer : public IPC::Channel::Listener, // Helper method to create the sync channel. void CreateChannel(); - std::string channel_name_; + IPC::ChannelHandle channel_handle_; scoped_ptr<IPC::SyncChannel> channel_; // Indicates whether a client is currently connected to the channel. bool client_connected_; diff --git a/chrome/service/service_process.cc b/chrome/service/service_process.cc index b49bdb1..9c0a8c8 100644 --- a/chrome/service/service_process.cc +++ b/chrome/service/service_process.cc @@ -205,19 +205,21 @@ bool ServiceProcess::Initialize(MessageLoopForUI* message_loop, } VLOG(1) << "Starting Service Process IPC Server"; - ipc_server_.reset(new ServiceIPCServer(GetServiceProcessChannelName())); + ServiceProcessState* state = ServiceProcessState::GetInstance(); + ipc_server_.reset(new ServiceIPCServer(state->GetServiceProcessChannel())); ipc_server_->Init(); // After the IPC server has started we signal that the service process is // ready. - if (!ServiceProcessState::GetInstance()->SignalReady( - io_thread_->message_loop_proxy(), - NewRunnableMethod(this, &ServiceProcess::Shutdown))) { + if (!state->SignalReady(io_thread_->message_loop_proxy(), + NewRunnableMethod(this, &ServiceProcess::Shutdown))) { return false; } // See if we need to stay running. ScheduleShutdownCheck(); + command_line_.reset(new CommandLine(command_line)); + return true; } @@ -307,7 +309,7 @@ void ServiceProcess::OnChromotingHostDisabled() { void ServiceProcess::OnServiceEnabled() { enabled_services_++; if (1 == enabled_services_) { - ServiceProcessState::GetInstance()->AddToAutoRun(); + ServiceProcessState::GetInstance()->AddToAutoRun(command_line_.get()); } } diff --git a/chrome/service/service_process.h b/chrome/service/service_process.h index 8a84efd..9d4d6f3 100644 --- a/chrome/service/service_process.h +++ b/chrome/service/service_process.h @@ -18,6 +18,7 @@ class ServiceProcessPrefs; class ServiceIPCServer; +class CommandLine; namespace net { class NetworkChangeNotifier; @@ -132,6 +133,7 @@ class ServiceProcess : public CloudPrintProxy::Client, #if defined(ENABLE_REMOTING) scoped_refptr<remoting::ChromotingHostManager> remoting_host_manager_; #endif + scoped_ptr<CommandLine> command_line_; DISALLOW_COPY_AND_ASSIGN(ServiceProcess); }; diff --git a/ipc/ipc_channel_posix.cc b/ipc/ipc_channel_posix.cc index 07d205a..ab8a06c 100644 --- a/ipc/ipc_channel_posix.cc +++ b/ipc/ipc_channel_posix.cc @@ -386,11 +386,11 @@ bool Channel::ChannelImpl::CreatePipe( #endif // IPC_USES_READWRITE } else if (mode_ & MODE_NAMED_FLAG) { // Case 2 from comment above. - must_unlink_ = true; if (mode_ & MODE_SERVER_FLAG) { if (!CreateServerUnixDomainSocket(pipe_name_, &pipe_)) { return false; } + must_unlink_ = true; } else if (mode_ & MODE_CLIENT_FLAG) { if (!CreateClientUnixDomainSocket(pipe_name_, &pipe_)) { return false; |