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/service/service_process.h" 6 7 #include <algorithm> 8 9 #include "base/basictypes.h" 10 #include "base/callback.h" 11 #include "base/command_line.h" 12 #include "base/environment.h" 13 #include "base/i18n/rtl.h" 14 #include "base/memory/singleton.h" 15 #include "base/path_service.h" 16 #include "base/prefs/json_pref_store.h" 17 #include "base/strings/string16.h" 18 #include "base/strings/utf_string_conversions.h" 19 #include "base/values.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/env_vars.h" 24 #include "chrome/common/pref_names.h" 25 #include "chrome/common/service_process_util.h" 26 #include "chrome/service/cloud_print/cloud_print_proxy.h" 27 #include "chrome/service/net/service_url_request_context.h" 28 #include "chrome/service/service_ipc_server.h" 29 #include "chrome/service/service_process_prefs.h" 30 #include "grit/chromium_strings.h" 31 #include "grit/generated_resources.h" 32 #include "net/base/network_change_notifier.h" 33 #include "net/url_request/url_fetcher.h" 34 #include "ui/base/l10n/l10n_util.h" 35 #include "ui/base/resource/resource_bundle.h" 36 #include "ui/base/ui_base_switches.h" 37 38 #if defined(TOOLKIT_GTK) 39 #include <gtk/gtk.h> 40 #include "ui/gfx/gtk_util.h" 41 #endif 42 43 ServiceProcess* g_service_process = NULL; 44 45 namespace { 46 47 // Delay in seconds after the last service is disabled before we attempt 48 // a shutdown. 49 const int kShutdownDelaySeconds = 60; 50 51 // Delay in hours between launching a browser process to check the 52 // policy for us. 53 const int64 kPolicyCheckDelayHours = 8; 54 55 const char kDefaultServiceProcessLocale[] = "en-US"; 56 57 class ServiceIOThread : public base::Thread { 58 public: 59 explicit ServiceIOThread(const char* name); 60 virtual ~ServiceIOThread(); 61 62 protected: 63 virtual void CleanUp() OVERRIDE; 64 65 private: 66 DISALLOW_COPY_AND_ASSIGN(ServiceIOThread); 67 }; 68 69 ServiceIOThread::ServiceIOThread(const char* name) : base::Thread(name) {} 70 ServiceIOThread::~ServiceIOThread() { 71 Stop(); 72 } 73 74 void ServiceIOThread::CleanUp() { 75 net::URLFetcher::CancelAll(); 76 } 77 78 // Prepares the localized strings that are going to be displayed to 79 // the user if the service process dies. These strings are stored in the 80 // environment block so they are accessible in the early stages of the 81 // chrome executable's lifetime. 82 void PrepareRestartOnCrashEnviroment( 83 const CommandLine &parsed_command_line) { 84 scoped_ptr<base::Environment> env(base::Environment::Create()); 85 // Clear this var so child processes don't show the dialog by default. 86 env->UnSetVar(env_vars::kShowRestart); 87 88 // For non-interactive tests we don't restart on crash. 89 if (env->HasVar(env_vars::kHeadless)) 90 return; 91 92 // If the known command-line test options are used we don't create the 93 // environment block which means we don't get the restart dialog. 94 if (parsed_command_line.HasSwitch(switches::kNoErrorDialogs)) 95 return; 96 97 // The encoding we use for the info is "title|context|direction" where 98 // direction is either env_vars::kRtlLocale or env_vars::kLtrLocale depending 99 // on the current locale. 100 string16 dlg_strings(l10n_util::GetStringUTF16(IDS_CRASH_RECOVERY_TITLE)); 101 dlg_strings.push_back('|'); 102 string16 adjusted_string( 103 l10n_util::GetStringFUTF16(IDS_SERVICE_CRASH_RECOVERY_CONTENT, 104 l10n_util::GetStringUTF16(IDS_GOOGLE_CLOUD_PRINT))); 105 base::i18n::AdjustStringForLocaleDirection(&adjusted_string); 106 dlg_strings.append(adjusted_string); 107 dlg_strings.push_back('|'); 108 dlg_strings.append(ASCIIToUTF16( 109 base::i18n::IsRTL() ? env_vars::kRtlLocale : env_vars::kLtrLocale)); 110 111 env->SetVar(env_vars::kRestartInfo, UTF16ToUTF8(dlg_strings)); 112 } 113 114 } // namespace 115 116 ServiceProcess::ServiceProcess() 117 : shutdown_event_(true, false), 118 main_message_loop_(NULL), 119 enabled_services_(0), 120 update_available_(false) { 121 DCHECK(!g_service_process); 122 g_service_process = this; 123 } 124 125 bool ServiceProcess::Initialize(base::MessageLoopForUI* message_loop, 126 const CommandLine& command_line, 127 ServiceProcessState* state) { 128 #if defined(TOOLKIT_GTK) 129 // TODO(jamiewalch): Calling GtkInitFromCommandLine here causes the process 130 // to abort if run headless. The correct fix for this is to refactor the 131 // service process to be more modular, a task that is currently underway. 132 // However, since this problem is blocking cloud print, the following quick 133 // hack will have to do. Note that the situation with this hack in place is 134 // no worse than it was when we weren't initializing GTK at all. 135 int argc = 1; 136 scoped_ptr<char*[]> argv(new char*[2]); 137 argv[0] = strdup(command_line.argv()[0].c_str()); 138 argv[1] = NULL; 139 char **argv_pointer = argv.get(); 140 gtk_init_check(&argc, &argv_pointer); 141 free(argv[0]); 142 #endif 143 main_message_loop_ = message_loop; 144 service_process_state_.reset(state); 145 network_change_notifier_.reset(net::NetworkChangeNotifier::Create()); 146 base::Thread::Options options; 147 options.message_loop_type = base::MessageLoop::TYPE_IO; 148 io_thread_.reset(new ServiceIOThread("ServiceProcess_IO")); 149 file_thread_.reset(new base::Thread("ServiceProcess_File")); 150 if (!io_thread_->StartWithOptions(options) || 151 !file_thread_->StartWithOptions(options)) { 152 NOTREACHED(); 153 Teardown(); 154 return false; 155 } 156 blocking_pool_ = new base::SequencedWorkerPool(3, "ServiceBlocking"); 157 158 request_context_getter_ = new ServiceURLRequestContextGetter(); 159 160 base::FilePath user_data_dir; 161 PathService::Get(chrome::DIR_USER_DATA, &user_data_dir); 162 base::FilePath pref_path = 163 user_data_dir.Append(chrome::kServiceStateFileName); 164 service_prefs_.reset(new ServiceProcessPrefs( 165 pref_path, 166 JsonPrefStore::GetTaskRunnerForFile(pref_path, blocking_pool_.get()) 167 .get())); 168 service_prefs_->ReadPrefs(); 169 170 // This switch it required to run connector with test gaia. 171 if (command_line.HasSwitch(switches::kIgnoreUrlFetcherCertRequests)) 172 net::URLFetcher::SetIgnoreCertificateRequests(true); 173 174 // Check if a locale override has been specified on the command-line. 175 std::string locale = command_line.GetSwitchValueASCII(switches::kLang); 176 if (!locale.empty()) { 177 service_prefs_->SetString(prefs::kApplicationLocale, locale); 178 service_prefs_->WritePrefs(); 179 } else { 180 // If no command-line value was specified, read the last used locale from 181 // the prefs. 182 locale = 183 service_prefs_->GetString(prefs::kApplicationLocale, std::string()); 184 // If no locale was specified anywhere, use the default one. 185 if (locale.empty()) 186 locale = kDefaultServiceProcessLocale; 187 } 188 ResourceBundle::InitSharedInstanceWithLocale(locale, NULL); 189 190 PrepareRestartOnCrashEnviroment(command_line); 191 192 // Enable Cloud Print if needed. First check the command-line. 193 // Then check if the cloud print proxy was previously enabled. 194 if (command_line.HasSwitch(switches::kEnableCloudPrintProxy) || 195 service_prefs_->GetBoolean(prefs::kCloudPrintProxyEnabled, false)) { 196 GetCloudPrintProxy()->EnableForUser(); 197 } 198 199 VLOG(1) << "Starting Service Process IPC Server"; 200 ipc_server_.reset(new ServiceIPCServer( 201 service_process_state_->GetServiceProcessChannel())); 202 ipc_server_->Init(); 203 204 // After the IPC server has started we signal that the service process is 205 // ready. 206 if (!service_process_state_->SignalReady( 207 io_thread_->message_loop_proxy().get(), 208 base::Bind(&ServiceProcess::Terminate, base::Unretained(this)))) { 209 return false; 210 } 211 212 // See if we need to stay running. 213 ScheduleShutdownCheck(); 214 215 // Occasionally check to see if we need to launch the browser to get the 216 // policy state information. 217 CloudPrintPolicyCheckIfNeeded(); 218 return true; 219 } 220 221 bool ServiceProcess::Teardown() { 222 service_prefs_.reset(); 223 cloud_print_proxy_.reset(); 224 225 ipc_server_.reset(); 226 // Signal this event before shutting down the service process. That way all 227 // background threads can cleanup. 228 shutdown_event_.Signal(); 229 io_thread_.reset(); 230 file_thread_.reset(); 231 232 if (blocking_pool_.get()) { 233 // The goal is to make it impossible for chrome to 'infinite loop' during 234 // shutdown, but to reasonably expect that all BLOCKING_SHUTDOWN tasks 235 // queued during shutdown get run. There's nothing particularly scientific 236 // about the number chosen. 237 const int kMaxNewShutdownBlockingTasks = 1000; 238 blocking_pool_->Shutdown(kMaxNewShutdownBlockingTasks); 239 blocking_pool_ = NULL; 240 } 241 242 // The NetworkChangeNotifier must be destroyed after all other threads that 243 // might use it have been shut down. 244 network_change_notifier_.reset(); 245 246 service_process_state_->SignalStopped(); 247 return true; 248 } 249 250 // This method is called when a shutdown command is received from IPC channel 251 // or there was an error in the IPC channel. 252 void ServiceProcess::Shutdown() { 253 #if defined(OS_MACOSX) 254 // On MacOS X the service must be removed from the launchd job list. 255 // http://www.chromium.org/developers/design-documents/service-processes 256 // The best way to do that is to go through the ForceServiceProcessShutdown 257 // path. If it succeeds Terminate() will be called from the handler registered 258 // via service_process_state_->SignalReady(). 259 // On failure call Terminate() directly to force the process to actually 260 // terminate. 261 if (!ForceServiceProcessShutdown("", 0)) { 262 Terminate(); 263 } 264 #else 265 Terminate(); 266 #endif 267 } 268 269 void ServiceProcess::Terminate() { 270 main_message_loop_->PostTask(FROM_HERE, base::MessageLoop::QuitClosure()); 271 } 272 273 bool ServiceProcess::HandleClientDisconnect() { 274 // If there are no enabled services or if there is an update available 275 // we want to shutdown right away. Else we want to keep listening for 276 // new connections. 277 if (!enabled_services_ || update_available()) { 278 Shutdown(); 279 return false; 280 } 281 return true; 282 } 283 284 cloud_print::CloudPrintProxy* ServiceProcess::GetCloudPrintProxy() { 285 if (!cloud_print_proxy_.get()) { 286 cloud_print_proxy_.reset(new cloud_print::CloudPrintProxy()); 287 cloud_print_proxy_->Initialize(service_prefs_.get(), this); 288 } 289 return cloud_print_proxy_.get(); 290 } 291 292 void ServiceProcess::OnCloudPrintProxyEnabled(bool persist_state) { 293 if (persist_state) { 294 // Save the preference that we have enabled the cloud print proxy. 295 service_prefs_->SetBoolean(prefs::kCloudPrintProxyEnabled, true); 296 service_prefs_->WritePrefs(); 297 } 298 OnServiceEnabled(); 299 } 300 301 void ServiceProcess::OnCloudPrintProxyDisabled(bool persist_state) { 302 if (persist_state) { 303 // Save the preference that we have disabled the cloud print proxy. 304 service_prefs_->SetBoolean(prefs::kCloudPrintProxyEnabled, false); 305 service_prefs_->WritePrefs(); 306 } 307 OnServiceDisabled(); 308 } 309 310 ServiceURLRequestContextGetter* 311 ServiceProcess::GetServiceURLRequestContextGetter() { 312 DCHECK(request_context_getter_.get()); 313 return request_context_getter_.get(); 314 } 315 316 void ServiceProcess::OnServiceEnabled() { 317 enabled_services_++; 318 if ((1 == enabled_services_) && 319 !CommandLine::ForCurrentProcess()->HasSwitch( 320 switches::kNoServiceAutorun)) { 321 if (!service_process_state_->AddToAutoRun()) { 322 // TODO(scottbyer/sanjeevr/dmaclach): Handle error condition 323 LOG(ERROR) << "Unable to AddToAutoRun"; 324 } 325 } 326 } 327 328 void ServiceProcess::OnServiceDisabled() { 329 DCHECK_NE(enabled_services_, 0); 330 enabled_services_--; 331 if (0 == enabled_services_) { 332 if (!service_process_state_->RemoveFromAutoRun()) { 333 // TODO(scottbyer/sanjeevr/dmaclach): Handle error condition 334 LOG(ERROR) << "Unable to RemoveFromAutoRun"; 335 } 336 // We will wait for some time to respond to IPCs before shutting down. 337 ScheduleShutdownCheck(); 338 } 339 } 340 341 void ServiceProcess::ScheduleShutdownCheck() { 342 base::MessageLoop::current()->PostDelayedTask( 343 FROM_HERE, 344 base::Bind(&ServiceProcess::ShutdownIfNeeded, base::Unretained(this)), 345 base::TimeDelta::FromSeconds(kShutdownDelaySeconds)); 346 } 347 348 void ServiceProcess::ShutdownIfNeeded() { 349 if (0 == enabled_services_) { 350 if (ipc_server_->is_client_connected()) { 351 // If there is a client connected, we need to try again later. 352 // Note that there is still a timing window here because a client may 353 // decide to connect at this point. 354 // TODO(sanjeevr): Fix this timing window. 355 ScheduleShutdownCheck(); 356 } else { 357 Shutdown(); 358 } 359 } 360 } 361 362 void ServiceProcess::ScheduleCloudPrintPolicyCheck() { 363 base::MessageLoop::current()->PostDelayedTask( 364 FROM_HERE, 365 base::Bind(&ServiceProcess::CloudPrintPolicyCheckIfNeeded, 366 base::Unretained(this)), 367 base::TimeDelta::FromHours(kPolicyCheckDelayHours)); 368 } 369 370 void ServiceProcess::CloudPrintPolicyCheckIfNeeded() { 371 if (enabled_services_ && !ipc_server_->is_client_connected()) { 372 GetCloudPrintProxy()->CheckCloudPrintProxyPolicy(); 373 } 374 ScheduleCloudPrintPolicyCheck(); 375 } 376 377 ServiceProcess::~ServiceProcess() { 378 Teardown(); 379 g_service_process = NULL; 380 } 381