Home | History | Annotate | Download | only in common
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include <algorithm>
      6 
      7 #include "base/command_line.h"
      8 #include "base/file_util.h"
      9 #include "base/logging.h"
     10 #include "base/mac/scoped_nsautorelease_pool.h"
     11 #include "base/memory/singleton.h"
     12 #include "base/path_service.h"
     13 #include "base/process_util.h"
     14 #include "base/sha1.h"
     15 #include "base/string16.h"
     16 #include "base/string_number_conversions.h"
     17 #include "base/string_util.h"
     18 #include "base/utf_string_conversions.h"
     19 #include "base/version.h"
     20 #include "chrome/common/chrome_constants.h"
     21 #include "chrome/common/chrome_paths.h"
     22 #include "chrome/common/chrome_switches.h"
     23 #include "chrome/common/chrome_version_info.h"
     24 #include "chrome/common/service_process_util.h"
     25 #include "content/common/child_process_host.h"
     26 
     27 #if !defined(OS_MACOSX)
     28 
     29 namespace {
     30 
     31 // This should be more than enough to hold a version string assuming each part
     32 // of the version string is an int64.
     33 const uint32 kMaxVersionStringLength = 256;
     34 
     35 // The structure that gets written to shared memory.
     36 struct ServiceProcessSharedData {
     37   char service_process_version[kMaxVersionStringLength];
     38   base::ProcessId service_process_pid;
     39 };
     40 
     41 // Gets the name of the shared memory used by the service process to write its
     42 // version. The name is not versioned.
     43 std::string GetServiceProcessSharedMemName() {
     44   return GetServiceProcessScopedName("_service_shmem");
     45 }
     46 
     47 enum ServiceProcessRunningState {
     48   SERVICE_NOT_RUNNING,
     49   SERVICE_OLDER_VERSION_RUNNING,
     50   SERVICE_SAME_VERSION_RUNNING,
     51   SERVICE_NEWER_VERSION_RUNNING,
     52 };
     53 
     54 ServiceProcessRunningState GetServiceProcessRunningState(
     55     std::string* service_version_out, base::ProcessId* pid_out) {
     56   std::string version;
     57   if (!GetServiceProcessData(&version, pid_out))
     58     return SERVICE_NOT_RUNNING;
     59 
     60 #if defined(OS_POSIX)
     61   // We only need to check for service running on POSIX because Windows cleans
     62   // up shared memory files when an app crashes, so there isn't a chance of
     63   // us reading bogus data from shared memory for an app that has died.
     64   if (!CheckServiceProcessReady()) {
     65     return SERVICE_NOT_RUNNING;
     66   }
     67 #endif  // defined(OS_POSIX)
     68 
     69   // At this time we have a version string. Set the out param if it exists.
     70   if (service_version_out)
     71     *service_version_out = version;
     72 
     73   scoped_ptr<Version> service_version(Version::GetVersionFromString(version));
     74   // If the version string is invalid, treat it like an older version.
     75   if (!service_version.get())
     76     return SERVICE_OLDER_VERSION_RUNNING;
     77 
     78   // Get the version of the currently *running* instance of Chrome.
     79   chrome::VersionInfo version_info;
     80   if (!version_info.is_valid()) {
     81     NOTREACHED() << "Failed to get current file version";
     82     // Our own version is invalid. This is an error case. Pretend that we
     83     // are out of date.
     84     return SERVICE_NEWER_VERSION_RUNNING;
     85   }
     86   scoped_ptr<Version> running_version(Version::GetVersionFromString(
     87       version_info.Version()));
     88   if (!running_version.get()) {
     89     NOTREACHED() << "Failed to parse version info";
     90     // Our own version is invalid. This is an error case. Pretend that we
     91     // are out of date.
     92     return SERVICE_NEWER_VERSION_RUNNING;
     93   }
     94 
     95   if (running_version->CompareTo(*service_version) > 0) {
     96     return SERVICE_OLDER_VERSION_RUNNING;
     97   } else if (service_version->CompareTo(*running_version) > 0) {
     98     return SERVICE_NEWER_VERSION_RUNNING;
     99   }
    100   return SERVICE_SAME_VERSION_RUNNING;
    101 }
    102 
    103 }  // namespace
    104 
    105 // Return a name that is scoped to this instance of the service process. We
    106 // use the hash of the user-data-dir as a scoping prefix. We can't use
    107 // the user-data-dir itself as we have limits on the size of the lock names.
    108 std::string GetServiceProcessScopedName(const std::string& append_str) {
    109   FilePath user_data_dir;
    110   PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
    111 #if defined(OS_WIN)
    112   std::string user_data_dir_path = WideToUTF8(user_data_dir.value());
    113 #elif defined(OS_POSIX)
    114   std::string user_data_dir_path = user_data_dir.value();
    115 #endif  // defined(OS_WIN)
    116   std::string hash = base::SHA1HashString(user_data_dir_path);
    117   std::string hex_hash = base::HexEncode(hash.c_str(), hash.length());
    118   return hex_hash + "." + append_str;
    119 }
    120 
    121 // Return a name that is scoped to this instance of the service process. We
    122 // use the user-data-dir and the version as a scoping prefix.
    123 std::string GetServiceProcessScopedVersionedName(
    124     const std::string& append_str) {
    125   std::string versioned_str;
    126   chrome::VersionInfo version_info;
    127   DCHECK(version_info.is_valid());
    128   versioned_str.append(version_info.Version());
    129   versioned_str.append(append_str);
    130   return GetServiceProcessScopedName(versioned_str);
    131 }
    132 
    133 // Reads the named shared memory to get the shared data. Returns false if no
    134 // matching shared memory was found.
    135 bool GetServiceProcessData(std::string* version, base::ProcessId* pid) {
    136   scoped_ptr<base::SharedMemory> shared_mem_service_data;
    137   shared_mem_service_data.reset(new base::SharedMemory());
    138   ServiceProcessSharedData* service_data = NULL;
    139   if (shared_mem_service_data.get() &&
    140       shared_mem_service_data->Open(GetServiceProcessSharedMemName(), true) &&
    141       shared_mem_service_data->Map(sizeof(ServiceProcessSharedData))) {
    142     service_data = reinterpret_cast<ServiceProcessSharedData*>(
    143         shared_mem_service_data->memory());
    144     // Make sure the version in shared memory is null-terminated. If it is not,
    145     // treat it as invalid.
    146     if (version && memchr(service_data->service_process_version, '\0',
    147                           sizeof(service_data->service_process_version)))
    148       *version = service_data->service_process_version;
    149     if (pid)
    150       *pid = service_data->service_process_pid;
    151     return true;
    152   }
    153   return false;
    154 }
    155 
    156 // Gets the name of the service process IPC channel.
    157 IPC::ChannelHandle GetServiceProcessChannel() {
    158   return GetServiceProcessScopedVersionedName("_service_ipc");
    159 }
    160 
    161 #endif  // !OS_MACOSX
    162 
    163 ServiceProcessState::ServiceProcessState() : state_(NULL) {
    164   CreateAutoRunCommandLine();
    165   CreateState();
    166 }
    167 
    168 ServiceProcessState::~ServiceProcessState() {
    169 #if !defined(OS_MACOSX)
    170   if (shared_mem_service_data_.get()) {
    171     shared_mem_service_data_->Delete(GetServiceProcessSharedMemName());
    172   }
    173 #endif  // !OS_MACOSX
    174   TearDownState();
    175 }
    176 
    177 void ServiceProcessState::SignalStopped() {
    178   TearDownState();
    179   shared_mem_service_data_.reset();
    180 }
    181 
    182 #if !defined(OS_MACOSX)
    183 bool ServiceProcessState::Initialize() {
    184   if (!TakeSingletonLock()) {
    185     return false;
    186   }
    187   // Now that we have the singleton, take care of killing an older version, if
    188   // it exists.
    189   if (!HandleOtherVersion())
    190     return false;
    191 
    192   // Write the version we are using to shared memory. This can be used by a
    193   // newer service to signal us to exit.
    194   return CreateSharedData();
    195 }
    196 
    197 bool ServiceProcessState::HandleOtherVersion() {
    198   std::string running_version;
    199   base::ProcessId process_id = 0;
    200   ServiceProcessRunningState state =
    201       GetServiceProcessRunningState(&running_version, &process_id);
    202   switch (state) {
    203     case SERVICE_SAME_VERSION_RUNNING:
    204     case SERVICE_NEWER_VERSION_RUNNING:
    205       return false;
    206     case SERVICE_OLDER_VERSION_RUNNING:
    207       // If an older version is running, kill it.
    208       ForceServiceProcessShutdown(running_version, process_id);
    209       break;
    210     case SERVICE_NOT_RUNNING:
    211       break;
    212   }
    213   return true;
    214 }
    215 
    216 bool ServiceProcessState::CreateSharedData() {
    217   chrome::VersionInfo version_info;
    218   if (!version_info.is_valid()) {
    219     NOTREACHED() << "Failed to get current file version";
    220     return false;
    221   }
    222   if (version_info.Version().length() >= kMaxVersionStringLength) {
    223     NOTREACHED() << "Version string length is << " <<
    224         version_info.Version().length() << "which is longer than" <<
    225         kMaxVersionStringLength;
    226     return false;
    227   }
    228 
    229   scoped_ptr<base::SharedMemory> shared_mem_service_data(
    230       new base::SharedMemory());
    231   if (!shared_mem_service_data.get())
    232     return false;
    233 
    234   uint32 alloc_size = sizeof(ServiceProcessSharedData);
    235   if (!shared_mem_service_data->CreateNamed(GetServiceProcessSharedMemName(),
    236                                             true, alloc_size))
    237     return false;
    238 
    239   if (!shared_mem_service_data->Map(alloc_size))
    240     return false;
    241 
    242   memset(shared_mem_service_data->memory(), 0, alloc_size);
    243   ServiceProcessSharedData* shared_data =
    244       reinterpret_cast<ServiceProcessSharedData*>(
    245           shared_mem_service_data->memory());
    246   memcpy(shared_data->service_process_version, version_info.Version().c_str(),
    247          version_info.Version().length());
    248   shared_data->service_process_pid = base::GetCurrentProcId();
    249   shared_mem_service_data_.reset(shared_mem_service_data.release());
    250   return true;
    251 }
    252 
    253 IPC::ChannelHandle ServiceProcessState::GetServiceProcessChannel() {
    254   return ::GetServiceProcessChannel();
    255 }
    256 
    257 #endif  // !OS_MACOSX
    258 
    259 void ServiceProcessState::CreateAutoRunCommandLine() {
    260   FilePath exe_path = ChildProcessHost::GetChildPath(false);
    261   if (exe_path.empty()) {
    262     NOTREACHED() << "Unable to get service process binary name.";
    263   }
    264   autorun_command_line_.reset(new CommandLine(exe_path));
    265   autorun_command_line_->AppendSwitchASCII(switches::kProcessType,
    266                                            switches::kServiceProcess);
    267 
    268   // The user data directory is the only other flag we currently want to
    269   // possibly store.
    270   const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess();
    271   FilePath user_data_dir =
    272     browser_command_line.GetSwitchValuePath(switches::kUserDataDir);
    273   if (!user_data_dir.empty())
    274     autorun_command_line_->AppendSwitchPath(switches::kUserDataDir,
    275                                             user_data_dir);
    276 }
    277