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