~ubuntu-branches/ubuntu/oneiric/mozc/oneiric

« back to all changes in this revision

Viewing changes to server/cache_service_manager.cc

  • Committer: Bazaar Package Importer
  • Author(s): Nobuhiro Iwamatsu
  • Date: 2010-07-14 03:26:47 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20100714032647-13qjisj6m8cm8jdx
Tags: 0.12.410.102-1
* New upstream release (Closes: #588971).
  - Add mozc-server, mozc-utils-gui and scim-mozc packages.
* Update debian/rules.
  Add --gypdir option to build_mozc.py.
* Update debian/control.
  - Bumped standards-version to 3.9.0.
  - Update description.
* Add mozc icon (Closes: #588972).
* Add patch which revises issue 18.
  ibus_mozc_issue18.patch
* kFreeBSD build support.
  support_kfreebsd.patch

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2010, Google Inc.
 
2
// All rights reserved.
 
3
//
 
4
// Redistribution and use in source and binary forms, with or without
 
5
// modification, are permitted provided that the following conditions are
 
6
// met:
 
7
//
 
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
 
13
// distribution.
 
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.
 
17
//
 
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.
 
29
 
 
30
#ifdef OS_WINDOWS
 
31
 
 
32
#include "server/cache_service_manager.h"
 
33
 
 
34
#include <windows.h>
 
35
#include <shlwapi.h>  // for SHLoadIndirectString
 
36
#include <strsafe.h>
 
37
#include <wincrypt.h>
 
38
#include <string>
 
39
 
 
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"
 
45
 
 
46
namespace mozc {
 
47
namespace {
 
48
const char    kProgramName[] = "GoogleIMEJaCacheService.exe";
 
49
const wchar_t kServiceName[] = L"GoogleIMEJaCacheService";
 
50
 
 
51
const uint64 kMinimumRequiredMemorySizeForInstall = 384 * 1024 * 1024;
 
52
 
 
53
class ScopedSCHandle {
 
54
 public:
 
55
  ScopedSCHandle()
 
56
      : handle_(NULL) {}
 
57
  explicit ScopedSCHandle(SC_HANDLE handle)
 
58
      : handle_(handle) {}
 
59
 
 
60
  ~ScopedSCHandle() {
 
61
    if (NULL != handle_) {
 
62
      ::CloseServiceHandle(handle_);
 
63
    }
 
64
  }
 
65
 
 
66
  SC_HANDLE get() const { return handle_; }
 
67
 
 
68
  void swap(ScopedSCHandle & other) {
 
69
    SC_HANDLE tmp = handle_;
 
70
    handle_ = other.handle_;
 
71
    other.handle_ = tmp;
 
72
  }
 
73
 
 
74
 private:
 
75
  SC_HANDLE handle_;
 
76
};
 
77
 
 
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' };
 
86
 
 
87
  HRESULT hr = ::StringCchPrintf(
 
88
      buffer, ARRAYSIZE(buffer), kRegistryStringRedirectionPattern,
 
89
      CacheServiceManager::GetUnquotedServicePath().c_str(), resource_id);
 
90
  if (hr != S_OK) {
 
91
    return L"";
 
92
  }
 
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.
 
97
    return redirector;
 
98
  }
 
99
 
 
100
  // If this program is running on Windows XP,
 
101
  // explicitly calls SHLoadIndirectString and returns the retrieved string
 
102
  // resource.
 
103
  wchar_t redirected_string[4096] = { L'\0' };
 
104
  hr = ::SHLoadIndirectString(redirector.c_str(), redirected_string,
 
105
      sizeof(redirected_string), NULL);
 
106
  if (hr != S_OK) {
 
107
    return L"";
 
108
  }
 
109
  return redirected_string;
 
110
}
 
111
 
 
112
wstring GetDisplayName() {
 
113
  return GetRegistryStringRedirectorOrRedirectedString(IDS_DISPLAYNAME);
 
114
}
 
115
 
 
116
wstring GetDescription() {
 
117
  return GetRegistryStringRedirectorOrRedirectedString(IDS_DESCRIPTION);
 
118
}
 
119
 
 
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,
 
124
                              wstring *dest) {
 
125
  if (dest == NULL) {
 
126
    return false;
 
127
  }
 
128
 
 
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";
 
133
    return false;
 
134
  }
 
135
 
 
136
  DWORD base64_string_len = 0;
 
137
  BOOL result = ::CryptBinaryToString(serialized.get(), serialized_len,
 
138
                                      CRYPT_STRING_BASE64, NULL,
 
139
                                      &base64_string_len);
 
140
  if (result == FALSE) {
 
141
    return false;
 
142
  }
 
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(),
 
146
                                 &base64_string_len);
 
147
  if (result == FALSE) {
 
148
    return false;
 
149
  }
 
150
  dest->assign(base64_string.get(), base64_string_len);
 
151
  return true;
 
152
}
 
153
 
 
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) {
 
161
    return false;
 
162
  }
 
163
 
 
164
  DWORD buffer_len = 0;
 
165
  BOOL result = ::CryptStringToBinary(src.c_str(),
 
166
                                      src.size(),
 
167
                                      CRYPT_STRING_BASE64,
 
168
                                      NULL,
 
169
                                      &buffer_len,
 
170
                                      NULL,
 
171
                                      NULL);
 
172
  if (result == FALSE) {
 
173
    return false;
 
174
  }
 
175
  scoped_array<BYTE> buffer(new BYTE[buffer_len]);
 
176
  result = ::CryptStringToBinary(src.c_str(),
 
177
                                 src.size(),
 
178
                                 CRYPT_STRING_BASE64,
 
179
                                 buffer.get(),
 
180
                                 &buffer_len,
 
181
                                 NULL,
 
182
                                 NULL);
 
183
  if (result == FALSE) {
 
184
    return false;
 
185
  }
 
186
  if (!message->ParseFromArray(buffer.get(), buffer_len)) {
 
187
    LOG(ERROR) << "ParseFromArray failed";
 
188
    return false;
 
189
  }
 
190
  return true;
 
191
}
 
192
 
 
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
 
197
//    retrieved handle.
 
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) {
 
204
    return false;
 
205
  }
 
206
 
 
207
  ScopedSCHandle sc_handle(::OpenSCManager(NULL, NULL,
 
208
                                           service_controler_rights));
 
209
  if (NULL == sc_handle.get()) {
 
210
    LOG(ERROR) << "OpenSCManager failed: " << ::GetLastError();
 
211
    return false;
 
212
  }
 
213
 
 
214
  ScopedSCHandle service_handle(::OpenService(
 
215
      sc_handle.get(), CacheServiceManager::GetServiceName(), service_rights));
 
216
  const int error = ::GetLastError();
 
217
 
 
218
  if (NULL == service_handle.get() && ERROR_SERVICE_DOES_NOT_EXIST != error) {
 
219
    LOG(ERROR) << "OpenService failed: " << error;
 
220
    return false;
 
221
  }
 
222
 
 
223
  // |service_handle| is null if and only if the cache service is not
 
224
  // installed.
 
225
  service_handle.swap(*handle);
 
226
  return true;
 
227
}
 
228
 
 
229
bool IsServiceRunning(const ScopedSCHandle &service_handle) {
 
230
  if (NULL == service_handle.get()) {
 
231
    return false;
 
232
  }
 
233
 
 
234
  SERVICE_STATUS service_status = { 0 };
 
235
  if (!::QueryServiceStatus(service_handle.get(), &service_status)) {
 
236
    LOG(ERROR) << "QueryServiceStatus failed: " << ::GetLastError();
 
237
    return false;
 
238
  }
 
239
 
 
240
  return service_status.dwCurrentState == SERVICE_RUNNING;
 
241
}
 
242
 
 
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();
 
248
      return false;
 
249
    }
 
250
    return true;
 
251
  }
 
252
 
 
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();
 
256
  }
 
257
  if (!::StartService(service_handle.get(), arguments.size(), args.get())) {
 
258
    LOG(ERROR) << "StartService failed: " << ::GetLastError();
 
259
    return false;
 
260
  }
 
261
 
 
262
  return true;
 
263
}
 
264
 
 
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();
 
269
    return false;
 
270
  }
 
271
  return true;
 
272
}
 
273
 
 
274
bool SetServiceDescription(const ScopedSCHandle &service_handle,
 
275
                           const wstring &description) {
 
276
  // +1 for '\0'
 
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';
 
281
 
 
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();
 
287
    return false;
 
288
  }
 
289
 
 
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
 
301
                                             TEXT("\0");
 
302
    if (!::ChangeServiceConfig2(service_handle.get(),
 
303
                                SERVICE_CONFIG_REQUIRED_PRIVILEGES_INFO,
 
304
                                &privileges_info)) {
 
305
      LOG(ERROR) << "ChangeServiceConfig2 failed: " << ::GetLastError();
 
306
      return false;
 
307
    }
 
308
 
 
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,
 
318
                                &sid_info)) {
 
319
      LOG(ERROR) << "ChangeServiceConfig2 failed: " << ::GetLastError();
 
320
      return false;
 
321
    }
 
322
  }
 
323
  return true;
 
324
}
 
325
 
 
326
// This function updates the following settings of the cache service as
 
327
// specified.
 
328
//  - Display name
 
329
//  - Desctiption
 
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())) {
 
341
    return false;
 
342
  }
 
343
 
 
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();
 
349
    return false;
 
350
  }
 
351
 
 
352
  if (!SetServiceDescription(service_handle, GetDescription())) {
 
353
    return false;
 
354
  }
 
355
 
 
356
  const bool now_running = IsServiceRunning(service_handle);
 
357
 
 
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) {
 
366
        return false;
 
367
      }
 
368
    }
 
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);
 
373
  // Nothing to do.
 
374
  } else {
 
375
    return true;
 
376
  }
 
377
 
 
378
  assert(0);
 
379
  return false;
 
380
}
 
381
 
 
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) {
 
388
    return false;
 
389
  }
 
390
 
 
391
  if (NULL == service_handle.get()) {
 
392
    return false;
 
393
  }
 
394
 
 
395
  DWORD size = 0;
 
396
  if (!::QueryServiceConfig(service_handle.get(), NULL, 0, &size) &&
 
397
      ::GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
 
398
    LOG(ERROR) << "QueryServiceConfig failed: " << ::GetLastError();
 
399
    return false;
 
400
  }
 
401
 
 
402
  if (size == 0) {
 
403
    LOG(ERROR) << "buffer size is 0";
 
404
    return false;
 
405
  }
 
406
 
 
407
  scoped_array<char> buf(new char[size]);
 
408
  LPQUERY_SERVICE_CONFIG service_config =
 
409
      reinterpret_cast<LPQUERY_SERVICE_CONFIG>(buf.get());
 
410
 
 
411
  if (!::QueryServiceConfig(service_handle.get(),
 
412
                            service_config, size, &size)) {
 
413
    LOG(ERROR) << "QueryServiceConfig failed: " << ::GetLastError();
 
414
    return false;
 
415
  }
 
416
 
 
417
  buf.swap(*result);
 
418
  return true;
 
419
}
 
420
}  // anonymous namespace
 
421
 
 
422
bool CacheServiceManager::IsInstalled() {
 
423
  ScopedSCHandle service_handle;
 
424
  if (!GetCacheService(SC_MANAGER_CONNECT | GENERIC_READ, SERVICE_QUERY_STATUS,
 
425
                       &service_handle)
 
426
      || (NULL == service_handle.get())) {
 
427
    return false;
 
428
  }
 
429
  return true;
 
430
}
 
431
 
 
432
bool CacheServiceManager::IsRunning() {
 
433
  ScopedSCHandle service_handle;
 
434
  if (!GetCacheService(SC_MANAGER_CONNECT | GENERIC_READ, SERVICE_QUERY_STATUS,
 
435
                       &service_handle)
 
436
      || (NULL == service_handle.get())) {
 
437
    return false;
 
438
  }
 
439
  return IsServiceRunning(service_handle);
 
440
}
 
441
 
 
442
bool CacheServiceManager::IsEnabled() {
 
443
  ScopedSCHandle service_handle;
 
444
  if (!GetCacheService(SC_MANAGER_CONNECT | GENERIC_READ, SERVICE_QUERY_CONFIG,
 
445
                       &service_handle)
 
446
      || (NULL == service_handle.get())) {
 
447
    return false;
 
448
  }
 
449
 
 
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)) {
 
456
    return false;
 
457
  }
 
458
  const LPQUERY_SERVICE_CONFIG service_config =
 
459
      reinterpret_cast<const LPQUERY_SERVICE_CONFIG>(buffer.get());
 
460
  return service_config->dwStartType == SERVICE_AUTO_START;
 
461
}
 
462
 
 
463
const wchar_t *CacheServiceManager::GetServiceName() {
 
464
  return kServiceName;
 
465
}
 
466
 
 
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) {
 
472
    return L"";
 
473
  }
 
474
  return wlock_service_path;
 
475
}
 
476
 
 
477
wstring CacheServiceManager::GetQuotedServicePath() {
 
478
  return L"\"" + GetUnquotedServicePath() + L"\"";
 
479
}
 
480
 
 
481
bool CacheServiceManager::EnableAutostart() {
 
482
  if (!IsInstalled()) {
 
483
    return false;
 
484
  }
 
485
 
 
486
  const bool enough = CacheServiceManager::HasEnoughMemory();
 
487
 
 
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;
 
501
}
 
502
 
 
503
bool CacheServiceManager::DisableService() {
 
504
  if (!IsInstalled()) {
 
505
    return false;
 
506
  }
 
507
 
 
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);
 
517
}
 
518
 
 
519
bool CacheServiceManager::RestartService() {
 
520
  ScopedSCHandle service_handle;
 
521
  if (!GetCacheService(SC_MANAGER_CONNECT,
 
522
                       SERVICE_START | SERVICE_STOP | SERVICE_QUERY_STATUS,
 
523
                       &service_handle)
 
524
      || (NULL == service_handle.get())) {
 
525
    return false;
 
526
  }
 
527
 
 
528
  SERVICE_STATUS status;
 
529
  if (!::ControlService(service_handle.get(), SERVICE_CONTROL_STOP, &status)) {
 
530
    LOG(ERROR) << "ControlService failed: " << ::GetLastError();
 
531
  }
 
532
 
 
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();
 
538
      return false;
 
539
    }
 
540
    if (service_status.dwCurrentState != SERVICE_RUNNING) {
 
541
      break;
 
542
    }
 
543
    ::Sleep(200);  // wait for 0.2sec
 
544
  }
 
545
 
 
546
  return StartServiceInternal(service_handle, vector<wstring>());
 
547
}
 
548
 
 
549
bool CacheServiceManager::HasEnoughMemory() {
 
550
  return Util::GetTotalPhysicalMemory()
 
551
      >= kMinimumRequiredMemorySizeForInstall;
 
552
}
 
553
 
 
554
bool CacheServiceManager::BackupStateAsString(wstring *result) {
 
555
  if (result == NULL) {
 
556
    return false;
 
557
  }
 
558
  result->clear();
 
559
 
 
560
  cache_service::Win32ServiceState state;
 
561
  state.set_version(1);
 
562
 
 
563
  ScopedSCHandle service_handle;
 
564
  if (!GetCacheService(SC_MANAGER_CONNECT | GENERIC_READ,
 
565
                       SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG,
 
566
                       &service_handle)) {
 
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
 
569
    // occurred.
 
570
    return false;
 
571
  }
 
572
 
 
573
  // If the cachse service is actually installed, |service_handle| should have
 
574
  // non-null value.
 
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);
 
581
  } else {
 
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)) {
 
588
      return false;
 
589
    }
 
590
    const LPQUERY_SERVICE_CONFIG service_config =
 
591
        reinterpret_cast<const LPQUERY_SERVICE_CONFIG>(buffer.get());
 
592
    // retrieves the server load type.
 
593
    state.set_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));
 
598
  }
 
599
 
 
600
  if (!SerializeToBase64WString(state, result)) {
 
601
    return false;
 
602
  }
 
603
 
 
604
  return true;
 
605
}
 
606
 
 
607
bool CacheServiceManager::RestoreStateFromString(const wstring &serialized) {
 
608
  cache_service::Win32ServiceState state;
 
609
  if (!DeserializeFromBase64WString(serialized, &state)) {
 
610
    return false;
 
611
  }
 
612
 
 
613
  return RestoreStateInternal(state);
 
614
}
 
615
 
 
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)) {
 
621
    return false;
 
622
  }
 
623
 
 
624
  if (NULL == service_handle.get()) {
 
625
    return true;
 
626
  }
 
627
 
 
628
  if (!IsServiceRunning(service_handle)) {
 
629
    return true;
 
630
  }
 
631
 
 
632
  if (!StopService(service_handle)) {
 
633
    return false;
 
634
  }
 
635
 
 
636
  return IsServiceRunning(service_handle);
 
637
}
 
638
}  // mozc
 
639
#endif  // OS_WINDOWS