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
diff --git a/DEPS b/DEPS
index 2439936c..967fadb0 100644
--- a/DEPS
+++ b/DEPS
@@ -250,7 +250,7 @@
"/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..29b08ca0 100644
--- a/base/process_util.h
+++ b/base/process_util.h
@@ -162,9 +162,6 @@
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 @@
#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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
}
}
-#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 @@
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 @@
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 @@
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 @@
}
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 @@
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..ff6e514e 100644
--- a/chrome/browser/service/service_process_control_browsertest.cc
+++ b/chrome/browser/service/service_process_control_browsertest.cc
@@ -25,6 +25,13 @@
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 @@
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 @@
// 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, 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..37f6cc80 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 @@
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 @@
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 @@
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 @@
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 @@
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..2ad72d7e 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 @@
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 @@
// 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 @@
// 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 @@
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 @@
// 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 @@
}
}
-// 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 @@
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 @@
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..3263fb56
--- /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 @@
// 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 @@
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 @@
return 0;
}
+#endif // !OS_MACOSX
diff --git a/chrome/common/service_process_util_win.cc b/chrome/common/service_process_util_win.cc
index f9a18166..b617988 100644
--- a/chrome/common/service_process_util_win.cc
+++ b/chrome/common/service_process_util_win.cc
@@ -28,6 +28,10 @@
GetServiceProcessScopedVersionedName("_service_shutdown_evt"));
}
+std::string GetServiceProcessAutoRunKey() {
+ return GetServiceProcessScopedName("_service_run");
+}
+
class ServiceProcessShutdownMonitor
: public base::win::ObjectWatcher::Delegate {
public:
@@ -85,8 +89,14 @@
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 @@
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 @@
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 @@
}
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 @@
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..0777ec80 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 @@
// 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 @@
}
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::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 @@
#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 07d205a9b..ab8a06c 100644
--- a/ipc/ipc_channel_posix.cc
+++ b/ipc/ipc_channel_posix.cc
@@ -386,11 +386,11 @@
#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;