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