• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ServiceIOThread(const char * name)73 ServiceIOThread::ServiceIOThread(const char* name) : base::Thread(name) {}
~ServiceIOThread()74 ServiceIOThread::~ServiceIOThread() {
75   Stop();
76 }
77 
CleanUp()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.
PrepareRestartOnCrashEnviroment(const CommandLine & parsed_command_line)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 
ServiceProcess()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 
Initialize(base::MessageLoopForUI * message_loop,const CommandLine & command_line,ServiceProcessState * state)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 
Teardown()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.
Shutdown()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 
Terminate()279 void ServiceProcess::Terminate() {
280   main_message_loop_->PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
281 }
282 
HandleClientDisconnect()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 
GetCloudPrintProxy()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 
OnCloudPrintProxyEnabled(bool persist_state)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 
OnCloudPrintProxyDisabled(bool persist_state)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*
GetServiceURLRequestContextGetter()321 ServiceProcess::GetServiceURLRequestContextGetter() {
322   DCHECK(request_context_getter_.get());
323   return request_context_getter_.get();
324 }
325 
OnServiceEnabled()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 
OnServiceDisabled()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 
ScheduleShutdownCheck()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 
ShutdownIfNeeded()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 
ScheduleCloudPrintPolicyCheck()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 
CloudPrintPolicyCheckIfNeeded()380 void ServiceProcess::CloudPrintPolicyCheckIfNeeded() {
381   if (enabled_services_ && !ipc_server_->is_client_connected()) {
382     GetCloudPrintProxy()->CheckCloudPrintProxyPolicy();
383   }
384   ScheduleCloudPrintPolicyCheck();
385 }
386 
~ServiceProcess()387 ServiceProcess::~ServiceProcess() {
388   Teardown();
389   g_service_process = NULL;
390 }
391