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 "chrome/common/service_process_util.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/command_line.h"
     10 #include "base/logging.h"
     11 #include "base/memory/singleton.h"
     12 #include "base/path_service.h"
     13 #include "base/sha1.h"
     14 #include "base/strings/string16.h"
     15 #include "base/strings/string_number_conversions.h"
     16 #include "base/strings/string_util.h"
     17 #include "base/strings/utf_string_conversions.h"
     18 #include "base/version.h"
     19 #include "chrome/common/chrome_constants.h"
     20 #include "chrome/common/chrome_paths.h"
     21 #include "chrome/common/chrome_switches.h"
     22 #include "chrome/common/chrome_version_info.h"
     23 #include "components/cloud_devices/common/cloud_devices_switches.h"
     24 #include "content/public/common/content_paths.h"
     25 #include "google_apis/gaia/gaia_switches.h"
     26 #include "ui/base/ui_base_switches.h"
     27 
     28 #if !defined(OS_MACOSX)
     29 
     30 namespace {
     31 
     32 // This should be more than enough to hold a version string assuming each part
     33 // of the version string is an int64.
     34 const uint32 kMaxVersionStringLength = 256;
     35 
     36 // The structure that gets written to shared memory.
     37 struct ServiceProcessSharedData {
     38   char service_process_version[kMaxVersionStringLength];
     39   base::ProcessId service_process_pid;
     40 };
     41 
     42 // Gets the name of the shared memory used by the service process to write its
     43 // version. The name is not versioned.
     44 std::string GetServiceProcessSharedMemName() {
     45   return GetServiceProcessScopedName("_service_shmem");
     46 }
     47 
     48 enum ServiceProcessRunningState {
     49   SERVICE_NOT_RUNNING,
     50   SERVICE_OLDER_VERSION_RUNNING,
     51   SERVICE_SAME_VERSION_RUNNING,
     52   SERVICE_NEWER_VERSION_RUNNING,
     53 };
     54 
     55 ServiceProcessRunningState GetServiceProcessRunningState(
     56     std::string* service_version_out, base::ProcessId* pid_out) {
     57   std::string version;
     58   if (!GetServiceProcessData(&version, pid_out))
     59     return SERVICE_NOT_RUNNING;
     60 
     61 #if defined(OS_POSIX)
     62   // We only need to check for service running on POSIX because Windows cleans
     63   // up shared memory files when an app crashes, so there isn't a chance of
     64   // us reading bogus data from shared memory for an app that has died.
     65   if (!CheckServiceProcessReady()) {
     66     return SERVICE_NOT_RUNNING;
     67   }
     68 #endif  // defined(OS_POSIX)
     69 
     70   // At this time we have a version string. Set the out param if it exists.
     71   if (service_version_out)
     72     *service_version_out = version;
     73 
     74   Version service_version(version);
     75   // If the version string is invalid, treat it like an older version.
     76   if (!service_version.IsValid())
     77     return SERVICE_OLDER_VERSION_RUNNING;
     78 
     79   // Get the version of the currently *running* instance of Chrome.
     80   chrome::VersionInfo version_info;
     81   if (!version_info.is_valid()) {
     82     NOTREACHED() << "Failed to get current file version";
     83     // Our own version is invalid. This is an error case. Pretend that we
     84     // are out of date.
     85     return SERVICE_NEWER_VERSION_RUNNING;
     86   }
     87   Version running_version(version_info.Version());
     88   if (!running_version.IsValid()) {
     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   base::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 = base::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 #endif  // !OS_MACOSX
    157 
    158 scoped_ptr<base::CommandLine> CreateServiceProcessCommandLine() {
    159   base::FilePath exe_path;
    160   PathService::Get(content::CHILD_PROCESS_EXE, &exe_path);
    161   DCHECK(!exe_path.empty()) << "Unable to get service process binary name.";
    162   scoped_ptr<base::CommandLine> command_line(new base::CommandLine(exe_path));
    163   command_line->AppendSwitchASCII(switches::kProcessType,
    164                                   switches::kServiceProcess);
    165   static const char* const kSwitchesToCopy[] = {
    166     switches::kCloudPrintSetupProxy,
    167     switches::kCloudPrintURL,
    168     switches::kCloudPrintXmppEndpoint,
    169 #if defined(OS_WIN)
    170     switches::kEnableCloudPrintXps,
    171 #endif
    172     switches::kEnableLogging,
    173     switches::kIgnoreUrlFetcherCertRequests,
    174     switches::kLang,
    175     switches::kLoggingLevel,
    176     switches::kLsoUrl,
    177     switches::kNoServiceAutorun,
    178     switches::kUserDataDir,
    179     switches::kV,
    180     switches::kVModule,
    181     switches::kWaitForDebugger,
    182   };
    183 
    184   command_line->CopySwitchesFrom(*base::CommandLine::ForCurrentProcess(),
    185                                  kSwitchesToCopy,
    186                                  arraysize(kSwitchesToCopy));
    187   return command_line.Pass();
    188 }
    189 
    190 ServiceProcessState::ServiceProcessState() : state_(NULL) {
    191   autorun_command_line_ = CreateServiceProcessCommandLine();
    192   CreateState();
    193 }
    194 
    195 ServiceProcessState::~ServiceProcessState() {
    196 #if !defined(OS_MACOSX)
    197   if (shared_mem_service_data_.get()) {
    198     shared_mem_service_data_->Delete(GetServiceProcessSharedMemName());
    199   }
    200 #endif  // !OS_MACOSX
    201   TearDownState();
    202 }
    203 
    204 void ServiceProcessState::SignalStopped() {
    205   TearDownState();
    206   shared_mem_service_data_.reset();
    207 }
    208 
    209 #if !defined(OS_MACOSX)
    210 bool ServiceProcessState::Initialize() {
    211   if (!TakeSingletonLock()) {
    212     return false;
    213   }
    214   // Now that we have the singleton, take care of killing an older version, if
    215   // it exists.
    216   if (!HandleOtherVersion())
    217     return false;
    218 
    219   // Write the version we are using to shared memory. This can be used by a
    220   // newer service to signal us to exit.
    221   return CreateSharedData();
    222 }
    223 
    224 bool ServiceProcessState::HandleOtherVersion() {
    225   std::string running_version;
    226   base::ProcessId process_id = 0;
    227   ServiceProcessRunningState state =
    228       GetServiceProcessRunningState(&running_version, &process_id);
    229   switch (state) {
    230     case SERVICE_SAME_VERSION_RUNNING:
    231     case SERVICE_NEWER_VERSION_RUNNING:
    232       return false;
    233     case SERVICE_OLDER_VERSION_RUNNING:
    234       // If an older version is running, kill it.
    235       ForceServiceProcessShutdown(running_version, process_id);
    236       break;
    237     case SERVICE_NOT_RUNNING:
    238       break;
    239   }
    240   return true;
    241 }
    242 
    243 bool ServiceProcessState::CreateSharedData() {
    244   chrome::VersionInfo version_info;
    245   if (!version_info.is_valid()) {
    246     NOTREACHED() << "Failed to get current file version";
    247     return false;
    248   }
    249   if (version_info.Version().length() >= kMaxVersionStringLength) {
    250     NOTREACHED() << "Version string length is << " <<
    251         version_info.Version().length() << "which is longer than" <<
    252         kMaxVersionStringLength;
    253     return false;
    254   }
    255 
    256   scoped_ptr<base::SharedMemory> shared_mem_service_data(
    257       new base::SharedMemory());
    258   if (!shared_mem_service_data.get())
    259     return false;
    260 
    261   uint32 alloc_size = sizeof(ServiceProcessSharedData);
    262   // TODO(viettrungluu): Named shared memory is deprecated (crbug.com/345734).
    263   if (!shared_mem_service_data->CreateNamedDeprecated
    264           (GetServiceProcessSharedMemName(), true, alloc_size))
    265     return false;
    266 
    267   if (!shared_mem_service_data->Map(alloc_size))
    268     return false;
    269 
    270   memset(shared_mem_service_data->memory(), 0, alloc_size);
    271   ServiceProcessSharedData* shared_data =
    272       reinterpret_cast<ServiceProcessSharedData*>(
    273           shared_mem_service_data->memory());
    274   memcpy(shared_data->service_process_version, version_info.Version().c_str(),
    275          version_info.Version().length());
    276   shared_data->service_process_pid = base::GetCurrentProcId();
    277   shared_mem_service_data_.reset(shared_mem_service_data.release());
    278   return true;
    279 }
    280 
    281 IPC::ChannelHandle ServiceProcessState::GetServiceProcessChannel() {
    282   return ::GetServiceProcessChannel();
    283 }
    284 
    285 #endif  // !OS_MACOSX
    286