1
// Copyright 2010, Google Inc.
2
// All rights reserved.
4
// Redistribution and use in source and binary forms, with or without
5
// modification, are permitted provided that the following conditions are
8
// * Redistributions of source code must retain the above copyright
9
// notice, this list of conditions and the following disclaimer.
10
// * Redistributions in binary form must reproduce the above
11
// copyright notice, this list of conditions and the following disclaimer
12
// in the documentation and/or other materials provided with the
14
// * Neither the name of Google Inc. nor the names of its
15
// contributors may be used to endorse or promote products derived from
16
// this software without specific prior written permission.
18
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
#include "server/cache_service_manager.h"
35
#include <shlwapi.h> // for SHLoadIndirectString
40
#include "base/base.h"
41
#include "base/scoped_handle.h"
42
#include "base/util.h"
43
#include "server/mozc_cache_service_resource.h"
44
#include "server/win32_service_state.pb.h"
48
const char kProgramName[] = "GoogleIMEJaCacheService.exe";
49
const wchar_t kServiceName[] = L"GoogleIMEJaCacheService";
51
const uint64 kMinimumRequiredMemorySizeForInstall = 384 * 1024 * 1024;
53
class ScopedSCHandle {
57
explicit ScopedSCHandle(SC_HANDLE handle)
61
if (NULL != handle_) {
62
::CloseServiceHandle(handle_);
66
SC_HANDLE get() const { return handle_; }
68
void swap(ScopedSCHandle & other) {
69
SC_HANDLE tmp = handle_;
70
handle_ = other.handle_;
78
// Returns a redirector for the specified string resource in Vista or later.
79
// Returns a redirected string for the specified string resource in XP.
80
// Returns an empty string if any error occurs.
81
// See http://msdn.microsoft.com/en-us/library/dd374120.aspx for details.
82
wstring GetRegistryStringRedirectorOrRedirectedString(int resource_id) {
83
const wchar_t kRegistryStringRedirectionPattern[] = L"@%s,-%d";
84
const wstring &service_path = CacheServiceManager::GetUnquotedServicePath();
85
wchar_t buffer[MAX_PATH] = { L'\0' };
87
HRESULT hr = ::StringCchPrintf(
88
buffer, ARRAYSIZE(buffer), kRegistryStringRedirectionPattern,
89
CacheServiceManager::GetUnquotedServicePath().c_str(), resource_id);
93
const wstring redirector(buffer);
94
if (Util::IsVistaOrLater()) {
95
// If this program is running on Windows Vista or later,
96
// just returns the redirector.
100
// If this program is running on Windows XP,
101
// explicitly calls SHLoadIndirectString and returns the retrieved string
103
wchar_t redirected_string[4096] = { L'\0' };
104
hr = ::SHLoadIndirectString(redirector.c_str(), redirected_string,
105
sizeof(redirected_string), NULL);
109
return redirected_string;
112
wstring GetDisplayName() {
113
return GetRegistryStringRedirectorOrRedirectedString(IDS_DISPLAYNAME);
116
wstring GetDescription() {
117
return GetRegistryStringRedirectorOrRedirectedString(IDS_DESCRIPTION);
120
// This function serializes a given message (any type of protobuf message)
121
// as a wstring encoded by base64.
122
// Returns true if succeeds.
123
bool SerializeToBase64WString(const ::google::protobuf::Message &message,
129
const int serialized_len = message.ByteSize();
130
scoped_array<BYTE> serialized(new BYTE[serialized_len]);
131
if (!message.SerializeToArray(serialized.get(), serialized_len)) {
132
LOG(ERROR) << "SerializeAsArray failed";
136
DWORD base64_string_len = 0;
137
BOOL result = ::CryptBinaryToString(serialized.get(), serialized_len,
138
CRYPT_STRING_BASE64, NULL,
140
if (result == FALSE) {
143
scoped_array<wchar_t> base64_string(new wchar_t[base64_string_len]);
144
result = ::CryptBinaryToString(serialized.get(), serialized_len,
145
CRYPT_STRING_BASE64, base64_string.get(),
147
if (result == FALSE) {
150
dest->assign(base64_string.get(), base64_string_len);
154
// This function deserializes a message (any type of protobuf message)
155
// from the given wstring. This wstring should be generated by
156
// SerializeToBase64WString.
157
// Returns true if succeeds
158
bool DeserializeFromBase64WString(const wstring &src,
159
::google::protobuf::Message *message) {
160
if (message == NULL) {
164
DWORD buffer_len = 0;
165
BOOL result = ::CryptStringToBinary(src.c_str(),
172
if (result == FALSE) {
175
scoped_array<BYTE> buffer(new BYTE[buffer_len]);
176
result = ::CryptStringToBinary(src.c_str(),
183
if (result == FALSE) {
186
if (!message->ParseFromArray(buffer.get(), buffer_len)) {
187
LOG(ERROR) << "ParseFromArray failed";
193
// A helper function to retrieve a service handle of the cache service
194
// with specified access rights.
195
// Returns true if one of the following conditions is satisfied.
196
// - A valid service handle is retrieved. In this case, |handle| owns the
198
// - It turns out w/o any error that the cache service is not installed.
199
// In this case, |handle| owns a NULL handle.
200
// Otherwise, returns false.
201
bool GetCacheService(DWORD service_controler_rights, DWORD service_rights,
202
ScopedSCHandle *handle) {
203
if (NULL == handle) {
207
ScopedSCHandle sc_handle(::OpenSCManager(NULL, NULL,
208
service_controler_rights));
209
if (NULL == sc_handle.get()) {
210
LOG(ERROR) << "OpenSCManager failed: " << ::GetLastError();
214
ScopedSCHandle service_handle(::OpenService(
215
sc_handle.get(), CacheServiceManager::GetServiceName(), service_rights));
216
const int error = ::GetLastError();
218
if (NULL == service_handle.get() && ERROR_SERVICE_DOES_NOT_EXIST != error) {
219
LOG(ERROR) << "OpenService failed: " << error;
223
// |service_handle| is null if and only if the cache service is not
225
service_handle.swap(*handle);
229
bool IsServiceRunning(const ScopedSCHandle &service_handle) {
230
if (NULL == service_handle.get()) {
234
SERVICE_STATUS service_status = { 0 };
235
if (!::QueryServiceStatus(service_handle.get(), &service_status)) {
236
LOG(ERROR) << "QueryServiceStatus failed: " << ::GetLastError();
240
return service_status.dwCurrentState == SERVICE_RUNNING;
243
bool StartServiceInternal(const ScopedSCHandle &service_handle,
244
const vector<wstring> &arguments) {
245
if (arguments.size() <= 0) {
246
if (!::StartService(service_handle.get(), 0, NULL)) {
247
LOG(ERROR) << "StartService failed: " << ::GetLastError();
253
scoped_array<const wchar_t*> args(new const wchar_t*[arguments.size()]);
254
for (size_t i = 0; i < arguments.size(); ++i) {
255
args[i] = arguments[i].c_str();
257
if (!::StartService(service_handle.get(), arguments.size(), args.get())) {
258
LOG(ERROR) << "StartService failed: " << ::GetLastError();
265
bool StopService(const ScopedSCHandle &service_handle) {
266
SERVICE_STATUS status;
267
if (!::ControlService(service_handle.get(), SERVICE_CONTROL_STOP, &status)) {
268
LOG(ERROR) << "ControlService failed: " << ::GetLastError();
274
bool SetServiceDescription(const ScopedSCHandle &service_handle,
275
const wstring &description) {
277
const size_t buffer_length = description.size() + 1;
278
scoped_array<wchar_t> buffer(new wchar_t[buffer_length]);
279
description._Copy_s(buffer.get(), buffer_length, description.size());
280
buffer[buffer_length - 1] = L'\0';
282
SERVICE_DESCRIPTION desc = { 0 };
283
desc.lpDescription = buffer.get();
284
if (!::ChangeServiceConfig2(service_handle.get(),
285
SERVICE_CONFIG_DESCRIPTION, &desc)) {
286
LOG(ERROR) << "ChangeServiceConfig2 failed: " << ::GetLastError();
290
// Windows Vista or later allows the SCM to run a service in a restricted
291
// context as described in following documents.
292
// http://msdn.microsoft.com/en-us/magazine/cc164252.aspx
293
// http://blogs.technet.com/richard_macdonald/archive/2007/06/27/1375523.aspx
294
// See also http://b/2470180
295
if (Util::IsVistaOrLater()) {
296
// Only SE_INC_BASE_PRIORITY_NAME and SE_CHANGE_NOTIFY are needed.
297
// According to MSDN Library, we need not explicitly specify the later.
298
// See http://msdn.microsoft.com/en-us/library/ms685976.aspx for details.
299
SERVICE_REQUIRED_PRIVILEGES_INFO privileges_info = { 0 };
300
privileges_info.pmszRequiredPrivileges = SE_INC_BASE_PRIORITY_NAME
302
if (!::ChangeServiceConfig2(service_handle.get(),
303
SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO,
305
LOG(ERROR) << "ChangeServiceConfig2 failed: " << ::GetLastError();
309
// Remove write privileges from the cache service.
310
// See http://msdn.microsoft.com/en-us/library/ms685987.aspx for details.
311
// This may restrict glog functions such as LOG(ERROR).
312
// TODO(yukawa): Output logging messages as debug strings, or output them
313
// to the Win32 event log.
314
SERVICE_SID_INFO sid_info = { 0 };
315
sid_info.dwServiceSidType = SERVICE_SID_TYPE_RESTRICTED;
316
if (!::ChangeServiceConfig2(service_handle.get(),
317
SERVICE_CONFIG_SERVICE_SID_INFO,
319
LOG(ERROR) << "ChangeServiceConfig2 failed: " << ::GetLastError();
326
// This function updates the following settings of the cache service as
330
// - Load type (regardless of the amount of the system memory)
331
// This function also starts or stops the cache service.
332
// Win32ServiceState::IsInstalled() will be ignored. You cannot use this
333
// function to install nor uninstall the cache service.
334
bool RestoreStateInternal(const cache_service::Win32ServiceState &state) {
335
ScopedSCHandle service_handle;
336
const DWORD kSCRights = SC_MANAGER_CONNECT;
337
const DWORD kServiceRights =
338
GENERIC_READ | GENERIC_WRITE | SERVICE_START | SERVICE_STOP;
339
if (!GetCacheService(kSCRights, kServiceRights, &service_handle)
340
|| (NULL == service_handle.get())) {
344
if (!::ChangeServiceConfig(service_handle.get(), SERVICE_NO_CHANGE,
345
static_cast<DWORD>(state.load_type()),
346
SERVICE_NO_CHANGE, NULL, NULL, NULL, NULL, NULL,
347
NULL, GetDisplayName().c_str())) {
348
LOG(ERROR) << "ChangeServiceConfig failed: " << ::GetLastError();
352
if (!SetServiceDescription(service_handle, GetDescription())) {
356
const bool now_running = IsServiceRunning(service_handle);
358
// If the service was runnning and is not running now, start it unless the
359
// load type is not DISABLED.
360
if (state.running() && !now_running &&
361
(state.load_type() != cache_service::Win32ServiceState::DISABLED)) {
362
vector<wstring> arguments(state.arguments_size());
363
arguments.resize(state.arguments_size());
364
for (size_t i = 0; i < state.arguments_size(); ++i) {
365
if (Util::UTF8ToWide(state.arguments(i).c_str(), &arguments[i]) <= 0) {
369
return StartServiceInternal(service_handle, arguments);
370
// If the service is now runnning and was not running, stop it.
371
} else if (!state.running() && now_running) {
372
return StopService(service_handle);
382
// Returns true if a byte array which contains valid QUERY_SERVICE_CONFIG
383
// is successfully retrieved. Some members of QUERY_SERVICE_CONFIG points
384
// memory addresses inside the retrieved byte array.
385
bool GetServiceConfig(const ScopedSCHandle &service_handle,
386
scoped_array<char> *result) {
387
if (NULL == result) {
391
if (NULL == service_handle.get()) {
396
if (!::QueryServiceConfig(service_handle.get(), NULL, 0, &size) &&
397
::GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
398
LOG(ERROR) << "QueryServiceConfig failed: " << ::GetLastError();
403
LOG(ERROR) << "buffer size is 0";
407
scoped_array<char> buf(new char[size]);
408
LPQUERY_SERVICE_CONFIG service_config =
409
reinterpret_cast<LPQUERY_SERVICE_CONFIG>(buf.get());
411
if (!::QueryServiceConfig(service_handle.get(),
412
service_config, size, &size)) {
413
LOG(ERROR) << "QueryServiceConfig failed: " << ::GetLastError();
420
} // anonymous namespace
422
bool CacheServiceManager::IsInstalled() {
423
ScopedSCHandle service_handle;
424
if (!GetCacheService(SC_MANAGER_CONNECT | GENERIC_READ, SERVICE_QUERY_STATUS,
426
|| (NULL == service_handle.get())) {
432
bool CacheServiceManager::IsRunning() {
433
ScopedSCHandle service_handle;
434
if (!GetCacheService(SC_MANAGER_CONNECT | GENERIC_READ, SERVICE_QUERY_STATUS,
436
|| (NULL == service_handle.get())) {
439
return IsServiceRunning(service_handle);
442
bool CacheServiceManager::IsEnabled() {
443
ScopedSCHandle service_handle;
444
if (!GetCacheService(SC_MANAGER_CONNECT | GENERIC_READ, SERVICE_QUERY_CONFIG,
446
|| (NULL == service_handle.get())) {
450
// This byte array will contain both QUERY_SERVICE_CONFIG and some
451
// string buffers which are reffered by corresponding members of
452
// QUERY_SERVICE_CONFIG. We have to keep the array until we complete
453
// all tasks which use the contents of QUERY_SERVICE_CONFIG.
454
scoped_array<char> buffer;
455
if (!GetServiceConfig(service_handle, &buffer)) {
458
const LPQUERY_SERVICE_CONFIG service_config =
459
reinterpret_cast<const LPQUERY_SERVICE_CONFIG>(buffer.get());
460
return service_config->dwStartType == SERVICE_AUTO_START;
463
const wchar_t *CacheServiceManager::GetServiceName() {
467
wstring CacheServiceManager::GetUnquotedServicePath() {
468
const string lock_service_path =
469
Util::JoinPath(Util::GetServerDirectory(), kProgramName);
470
wstring wlock_service_path;
471
if (Util::UTF8ToWide(lock_service_path.c_str(), &wlock_service_path) <= 0) {
474
return wlock_service_path;
477
wstring CacheServiceManager::GetQuotedServicePath() {
478
return L"\"" + GetUnquotedServicePath() + L"\"";
481
bool CacheServiceManager::EnableAutostart() {
482
if (!IsInstalled()) {
486
const bool enough = CacheServiceManager::HasEnoughMemory();
488
// Fortunately, the expected service settings can be descried by
489
// Win32ServiceState so we can use RestoreStateInternal to change the
490
// settings and start the service (if needed).
491
// If the system does not have enough memory, disable the service
492
// to reduce the performance impact on the boot-time.
493
cache_service::Win32ServiceState state;
494
state.set_version(1);
495
state.set_installed(true);
496
state.set_load_type(enough ? cache_service::Win32ServiceState::AUTO_START
497
: cache_service::Win32ServiceState::DISABLED);
498
state.set_running(enough);
499
const bool result = RestoreStateInternal(state);
500
return enough ? result : false;
503
bool CacheServiceManager::DisableService() {
504
if (!IsInstalled()) {
508
// Fortunately, the expected service settings can be descried by
509
// Win32ServiceState so we can use RestoreStateInternal to change the
510
// settings and stop the service (if needed).
511
cache_service::Win32ServiceState state;
512
state.set_version(1);
513
state.set_installed(true);
514
state.set_load_type(cache_service::Win32ServiceState::DISABLED);
515
state.set_running(false);
516
return RestoreStateInternal(state);
519
bool CacheServiceManager::RestartService() {
520
ScopedSCHandle service_handle;
521
if (!GetCacheService(SC_MANAGER_CONNECT,
522
SERVICE_START | SERVICE_STOP | SERVICE_QUERY_STATUS,
524
|| (NULL == service_handle.get())) {
528
SERVICE_STATUS status;
529
if (!::ControlService(service_handle.get(), SERVICE_CONTROL_STOP, &status)) {
530
LOG(ERROR) << "ControlService failed: " << ::GetLastError();
533
const int kNumTrial = 10;
534
for (int i = 0; i < kNumTrial; ++i) {
535
SERVICE_STATUS service_status = { 0 };
536
if (!::QueryServiceStatus(service_handle.get(), &service_status)) {
537
LOG(ERROR) << "QueryServiceStatus failed: " << ::GetLastError();
540
if (service_status.dwCurrentState != SERVICE_RUNNING) {
543
::Sleep(200); // wait for 0.2sec
546
return StartServiceInternal(service_handle, vector<wstring>());
549
bool CacheServiceManager::HasEnoughMemory() {
550
return Util::GetTotalPhysicalMemory()
551
>= kMinimumRequiredMemorySizeForInstall;
554
bool CacheServiceManager::BackupStateAsString(wstring *result) {
555
if (result == NULL) {
560
cache_service::Win32ServiceState state;
561
state.set_version(1);
563
ScopedSCHandle service_handle;
564
if (!GetCacheService(SC_MANAGER_CONNECT | GENERIC_READ,
565
SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG,
567
// If it turns out w/o any error that the cache service is not installed,
568
// |GetCacheService| returns true. We can conclude any other error
573
// If the cachse service is actually installed, |service_handle| should have
575
state.set_installed(NULL != service_handle.get());
576
if (!state.installed()) {
577
// The cache service is not installed.
578
// Use default settings with setting |installed| flag to false.
579
state.set_load_type(cache_service::Win32ServiceState::AUTO_START);
580
state.set_running(true);
582
// This byte array will contain both QUERY_SERVICE_CONFIG and some
583
// string buffers which are reffered by corresponding members of
584
// QUERY_SERVICE_CONFIG. We have to keep the array until we complete
585
// all tasks which use the contents of QUERY_SERVICE_CONFIG.
586
scoped_array<char> buffer;
587
if (!GetServiceConfig(service_handle, &buffer)) {
590
const LPQUERY_SERVICE_CONFIG service_config =
591
reinterpret_cast<const LPQUERY_SERVICE_CONFIG>(buffer.get());
592
// retrieves the server load type.
594
static_cast<cache_service::Win32ServiceState::LoadType>(
595
service_config->dwStartType));
596
// retrieves the server running status.
597
state.set_running(IsServiceRunning(service_handle));
600
if (!SerializeToBase64WString(state, result)) {
607
bool CacheServiceManager::RestoreStateFromString(const wstring &serialized) {
608
cache_service::Win32ServiceState state;
609
if (!DeserializeFromBase64WString(serialized, &state)) {
613
return RestoreStateInternal(state);
616
bool CacheServiceManager::EnsureServiceStopped() {
617
ScopedSCHandle service_handle;
618
const DWORD kSCRights = SC_MANAGER_CONNECT;
619
const DWORD kServiceRights = GENERIC_READ | SERVICE_STOP;
620
if (!GetCacheService(kSCRights, kServiceRights, &service_handle)) {
624
if (NULL == service_handle.get()) {
628
if (!IsServiceRunning(service_handle)) {
632
if (!StopService(service_handle)) {
636
return IsServiceRunning(service_handle);