summaryrefslogtreecommitdiffstats
path: root/chrome/common/service_process_util.cc
diff options
context:
space:
mode:
authorsanjeevr@chromium.org <sanjeevr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-10-09 03:17:58 +0000
committersanjeevr@chromium.org <sanjeevr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-10-09 03:17:58 +0000
commit7b23c9b94767948f1759e59f779ffe6a09b55c34 (patch)
treeb484b34b2db6e9f35ec90aa79aadefc3c2d9f5c7 /chrome/common/service_process_util.cc
parentc3c1250bec563360a1b4fd409a54c14ec14a4413 (diff)
downloadchromium_src-7b23c9b94767948f1759e59f779ffe6a09b55c34.zip
chromium_src-7b23c9b94767948f1759e59f779ffe6a09b55c34.tar.gz
chromium_src-7b23c9b94767948f1759e59f779ffe6a09b55c34.tar.bz2
The IPC channel names and event names used by the service process now append the version string so that a browser with the same version only can talk to it.
In addition, the service process writes its version into shared memory (the name of this shared mem does not use a version) and also listens on a shutdown event. This can be used by a newer service process to shutdown an older version when it starts. BUG=None TEST=Unittests, with an existing service process running, let chrome autoupdate and then run the service process again. It should kill the old one. Review URL: http://codereview.chromium.org/3603016 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@62066 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/common/service_process_util.cc')
-rw-r--r--chrome/common/service_process_util.cc288
1 files changed, 255 insertions, 33 deletions
diff --git a/chrome/common/service_process_util.cc b/chrome/common/service_process_util.cc
index 5cf30df..5a664d9 100644
--- a/chrome/common/service_process_util.cc
+++ b/chrome/common/service_process_util.cc
@@ -5,75 +5,280 @@
#include "base/file_util.h"
#include "base/logging.h"
#include "base/path_service.h"
+#include "base/shared_memory.h"
+#include "base/string16.h"
+#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
+#include "chrome/common/chrome_version_info.h"
#include "chrome/common/service_process_util.h"
+#include "chrome/installer/util/version.h"
#if defined(OS_WIN)
+#include "base/object_watcher.h"
#include "base/scoped_handle_win.h"
#endif
// TODO(hclam): Split this file for different platforms.
-std::string GetServiceProcessChannelName() {
+
+namespace {
+
+// This should be more than enough to hold a version string assuming each part
+// of the version string is an int64.
+const uint32 kMaxVersionStringLength = 256;
+
+// 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) {
FilePath user_data_dir;
PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
-
- // TODO(sanjeevr): We need to actually figure out the right way to determine
- // a channel name. The below is to facilitate testing only.
#if defined(OS_WIN)
- std::string channel_name = WideToUTF8(user_data_dir.value());
+ std::string scoped_name = WideToUTF8(user_data_dir.value());
#elif defined(OS_POSIX)
- std::string channel_name = user_data_dir.value();
+ std::string scoped_name = user_data_dir.value();
#endif // defined(OS_WIN)
- std::replace(channel_name.begin(), channel_name.end(), '\\', '!');
- channel_name.append("_service_ipc");
- return channel_name;
+ std::replace(scoped_name.begin(), scoped_name.end(), '\\', '!');
+ scoped_name.append(append_str);
+ return scoped_name;
+}
+
+// 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) {
+ std::string versioned_str;
+ chrome::VersionInfo version_info;
+ DCHECK(version_info.is_valid());
+ versioned_str.append(version_info.Version());
+ versioned_str.append(append_str);
+ return GetServiceProcessScopedName(versioned_str);
+}
+
+// Gets the name of the shared memory used by the service process to write its
+// version. The name is not versioned.
+std::string GetServiceProcessSharedMemName() {
+ return GetServiceProcessScopedName("_service_shmem");
}
// Gets the name of the lock file for service process.
-static FilePath GetServiceProcessLockFilePath() {
+FilePath GetServiceProcessLockFilePath() {
FilePath user_data_dir;
PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
+ chrome::VersionInfo version_info;
+#if defined(OS_WIN)
+ user_data_dir.Append(ASCIIToWide(version_info.Version()));
+#elif defined(OS_POSIX)
+ user_data_dir.Append(version_info.Version());
+#endif // defined(OS_WIN)
return user_data_dir.Append(FILE_PATH_LITERAL("Service Process Lock"));
}
+enum ServiceProcessRunningState {
+ SERVICE_NOT_RUNNING,
+ SERVICE_OLDER_VERSION_RUNNING,
+ SERVICE_SAME_VERSION_RUNNING,
+ SERVICE_NEWER_VERSION_RUNNING,
+};
+
+ServiceProcessRunningState GetServiceProcessRunningState(
+ std::string* service_version_out) {
+ std::string version;
+ scoped_ptr<base::SharedMemory> shared_mem_version;
+ shared_mem_version.reset(new base::SharedMemory());
+ if (shared_mem_version.get() &&
+ shared_mem_version->Open(GetServiceProcessSharedMemName(), true) &&
+ shared_mem_version->Map(kMaxVersionStringLength) &&
+ memchr(shared_mem_version->memory(), '\0', kMaxVersionStringLength)) {
+ version =
+ static_cast<const char *>(shared_mem_version->memory());
+ } else {
+ return SERVICE_NOT_RUNNING;
+ }
+ // At this time we have a version string. Set the out param if it exists.
+ if (service_version_out)
+ *service_version_out = version;
+
+ scoped_ptr<installer::Version> service_version;
+ service_version.reset(
+ installer::Version::GetVersionFromString(ASCIIToUTF16(version)));
+ // If the version string is invalid, treat it like an older version.
+ if (!service_version.get())
+ return SERVICE_OLDER_VERSION_RUNNING;
+
+ // Get the version of the currently *running* instance of Chrome.
+ chrome::VersionInfo version_info;
+ if (!version_info.is_valid()) {
+ NOTREACHED() << "Failed to get current file version";
+ // Our own version is invalid. This is an error case. Pretend that we
+ // are out of date.
+ return SERVICE_NEWER_VERSION_RUNNING;
+ }
+ scoped_ptr<installer::Version> running_version(
+ installer::Version::GetVersionFromString(
+ ASCIIToUTF16(version_info.Version())));
+ if (!running_version.get()) {
+ NOTREACHED() << "Failed to parse version info";
+ // Our own version is invalid. This is an error case. Pretend that we
+ // are out of date.
+ return SERVICE_NEWER_VERSION_RUNNING;
+ }
+
+ if (running_version->IsHigherThan(service_version.get())) {
+ return SERVICE_OLDER_VERSION_RUNNING;
+ } else if (service_version->IsHigherThan(running_version.get())) {
+ return SERVICE_NEWER_VERSION_RUNNING;
+ }
+ return SERVICE_SAME_VERSION_RUNNING;
+}
+
#if defined(OS_WIN)
-static std::wstring GetServiceProcessEventName() {
- FilePath path = GetServiceProcessLockFilePath();
- std::wstring event_name = path.value();
- std::replace(event_name.begin(), event_name.end(), '\\', '!');
- return event_name;
+string16 GetServiceProcessEventName() {
+ return UTF8ToWide(
+ GetServiceProcessScopedVersionedName("Service Process Lock"));
}
-// An event that is signaled when a service process is running. This
-// variable is used only in the service process.
-static HANDLE g_service_process_running_event;
-#endif
+string16 GetServiceProcessShutdownEventName() {
+ return UTF8ToWide(
+ GetServiceProcessScopedVersionedName("_service_shutdown_evt"));
+}
+
+class ServiceProcessShutdownMonitor : public base::ObjectWatcher::Delegate {
+ public:
+ explicit ServiceProcessShutdownMonitor(Task* shutdown_task)
+ : shutdown_task_(shutdown_task) {
+ }
+ void Start() {
+ string16 event_name = GetServiceProcessShutdownEventName();
+ CHECK(event_name.length() <= MAX_PATH);
+ shutdown_event_.Set(CreateEvent(NULL, TRUE, FALSE, event_name.c_str()));
+ watcher_.StartWatching(shutdown_event_.Get(), this);
+ }
+
+ // base::ObjectWatcher::Delegate implementation.
+ virtual void OnObjectSignaled(HANDLE object) {
+ shutdown_task_->Run();
+ shutdown_task_.reset();
+ }
+
+ private:
+ ScopedHandle shutdown_event_;
+ base::ObjectWatcher watcher_;
+ scoped_ptr<Task> shutdown_task_;
+};
+
+#endif // defined(OS_WIN)
+
+struct ServiceProcessGlobalState {
+ scoped_ptr<base::SharedMemory> shared_mem_version;
+#if defined(OS_WIN)
+ // An event that is signaled when a service process is running.
+ ScopedHandle running_event;
+ scoped_ptr<ServiceProcessShutdownMonitor> shutdown_monitor;
+#endif // defined(OS_WIN)
+};
+
+// TODO(sanjeevr): Remove this ugly global pointer and move all methods and
+// data members used by the service process into one singleton class which
+// could be owned by the service process.
+ServiceProcessGlobalState* g_service_globals = NULL;
+
+} // namespace
+
+// Gets the name of the service process IPC channel.
+std::string GetServiceProcessChannelName() {
+ return GetServiceProcessScopedVersionedName("_service_ipc");
+}
+
+std::string GetServiceProcessAutoRunKey() {
+ return GetServiceProcessScopedName("_service_run");
+}
bool TakeServiceProcessSingletonLock() {
+ DCHECK(g_service_globals == NULL);
+ std::string running_version;
+ ServiceProcessRunningState state =
+ GetServiceProcessRunningState(&running_version);
+ switch (state) {
+ case SERVICE_SAME_VERSION_RUNNING:
+ case SERVICE_NEWER_VERSION_RUNNING:
+ return false;
+ case SERVICE_OLDER_VERSION_RUNNING:
+ // If an older version is running, kill it.
+ ForceServiceProcessShutdown(running_version);
+ break;
+ case SERVICE_NOT_RUNNING:
+ break;
+ }
+ g_service_globals = new ServiceProcessGlobalState;
+ // TODO(sanjeevr): We can probably use the shared mem as the sole singleton
+ // mechanism. For that the shared mem class needs to return whether it created
+ // new instance or opened an existing one.
#if defined(OS_WIN)
- std::wstring event_name = GetServiceProcessEventName();
- DCHECK(g_service_process_running_event == NULL);
+ string16 event_name = GetServiceProcessEventName();
+ CHECK(event_name.length() <= MAX_PATH);
ScopedHandle service_process_running_event;
service_process_running_event.Set(
CreateEvent(NULL, TRUE, FALSE, event_name.c_str()));
DWORD error = GetLastError();
- if ((error == ERROR_ALREADY_EXISTS) || (error == ERROR_ACCESS_DENIED))
+ if ((error == ERROR_ALREADY_EXISTS) || (error == ERROR_ACCESS_DENIED)) {
+ delete g_service_globals;
+ g_service_globals = NULL;
return false;
+ }
DCHECK(service_process_running_event.IsValid());
- g_service_process_running_event = service_process_running_event.Take();
-#endif
+ g_service_globals->running_event.Set(service_process_running_event.Take());
+#else
// TODO(sanjeevr): Implement singleton mechanism for other platforms.
+ NOTIMPLEMENTED();
+#endif
+ // Now that we have the singleton, let is also write the version we are using
+ // to shared memory. This can be used by a newer service to signal us to exit.
+ chrome::VersionInfo version_info;
+ if (!version_info.is_valid()) {
+ NOTREACHED() << "Failed to get current file version";
+ return false;
+ }
+ if (version_info.Version().length() >= kMaxVersionStringLength) {
+ NOTREACHED() << "Version string length is << " <<
+ version_info.Version().length() << "which is longer than" <<
+ kMaxVersionStringLength;
+ return false;
+ }
+
+ scoped_ptr<base::SharedMemory> shared_mem_version;
+ shared_mem_version.reset(new base::SharedMemory());
+ if (!shared_mem_version.get())
+ return false;
+
+ uint32 alloc_size = kMaxVersionStringLength;
+ if (!shared_mem_version->Create(GetServiceProcessSharedMemName(), false,
+ true, alloc_size))
+ return false;
+
+ if (!shared_mem_version->Map(alloc_size))
+ return false;
+
+ memset(shared_mem_version->memory(), 0, alloc_size);
+ memcpy(shared_mem_version->memory(), version_info.Version().c_str(),
+ version_info.Version().length());
+ g_service_globals->shared_mem_version.reset(shared_mem_version.release());
return true;
}
-void SignalServiceProcessRunning() {
+void SignalServiceProcessRunning(Task* shutdown_task) {
#if defined(OS_WIN)
- DCHECK(g_service_process_running_event != NULL);
- SetEvent(g_service_process_running_event);
+ DCHECK(g_service_globals != NULL);
+ DCHECK(g_service_globals->running_event.IsValid());
+ SetEvent(g_service_globals->running_event.Get());
+ g_service_globals->shutdown_monitor.reset(
+ new ServiceProcessShutdownMonitor(shutdown_task));
+ g_service_globals->shutdown_monitor->Start();
#else
// TODO(hclam): Implement better mechanism for these platform.
+ // Also we need to save shutdown task. For now we just delete the shutdown
+ // task because we have not way to listen for shutdown requests.
+ delete shutdown_task;
const FilePath path = GetServiceProcessLockFilePath();
FILE* file = file_util::OpenFile(path, "wb+");
if (!file)
@@ -84,20 +289,36 @@ void SignalServiceProcessRunning() {
}
void SignalServiceProcessStopped() {
-#if defined(OS_WIN)
- // Close the handle to the event.
- CloseHandle(g_service_process_running_event);
- g_service_process_running_event = NULL;
-#else
+ delete g_service_globals;
+ g_service_globals = NULL;
+#if !defined(OS_WIN)
// TODO(hclam): Implement better mechanism for these platform.
const FilePath path = GetServiceProcessLockFilePath();
file_util::Delete(path, false);
#endif
}
+bool ForceServiceProcessShutdown(const std::string& version) {
+#if defined(OS_WIN)
+ ScopedHandle shutdown_event;
+ std::string versioned_name = version;
+ versioned_name.append("_service_shutdown_evt");
+ string16 event_name =
+ UTF8ToWide(GetServiceProcessScopedName(versioned_name));
+ shutdown_event.Set(OpenEvent(EVENT_MODIFY_STATE, FALSE, event_name.c_str()));
+ if (!shutdown_event.IsValid())
+ return false;
+ SetEvent(shutdown_event.Get());
+ return true;
+#else // defined(OS_WIN)
+ NOTIMPLEMENTED();
+ return false;
+#endif // defined(OS_WIN)
+}
+
bool CheckServiceProcessRunning() {
#if defined(OS_WIN)
- std::wstring event_name = GetServiceProcessEventName();
+ string16 event_name = GetServiceProcessEventName();
ScopedHandle event(
OpenEvent(SYNCHRONIZE | READ_CONTROL, false, event_name.c_str()));
if (!event.IsValid())
@@ -110,3 +331,4 @@ bool CheckServiceProcessRunning() {
return file_util::PathExists(path);
#endif
}
+