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