1 // Copyright 2010-2018, 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_WIN
31 #include <string.h>
32 #include <windows.h>
33 #include <sddl.h>
34 #include <shlobj.h>
35 #else
36 #include <unistd.h>
37 #endif // OS_WIN
38
39 #include <cstring>
40 #include <string>
41
42 #include "base/const.h"
43 #include "base/file_stream.h"
44 #include "base/file_util.h"
45 #include "base/logging.h"
46 #include "base/mac_util.h"
47 #include "base/port.h"
48 #include "base/process.h"
49 #include "base/run_level.h"
50 #include "base/system_util.h"
51 #include "base/util.h"
52 #include "client/client.h"
53 #include "client/client_interface.h"
54 #include "ipc/ipc.h"
55 #include "ipc/named_event.h"
56
57 #ifdef OS_WIN
58 #include "base/win_sandbox.h"
59 #endif // OS_WIN
60
61 namespace mozc {
62 namespace client {
63 namespace {
64 const char kServerName[] = "session";
65
66 // Wait at most kServerWaitTimeout msec until server gets ready
67 const uint32 kServerWaitTimeout = 20000; // 20 sec
68
69 // for every 1000m sec, check server
70 const uint32 kRetryIntervalForServer = 1000;
71
72 // Try 20 times to check mozc_server is running
73 const uint32 kTrial = 20;
74
75 #ifdef DEBUG
76 // Load special flags for server.
77 // This should be enabled on debug build
LoadServerFlags()78 const string LoadServerFlags() {
79 const char kServerFlagsFile[] = "mozc_server_flags.txt";
80 const string filename = FileUtil::JoinPath(
81 SystemUtil::GetUserProfileDirectory(), kServerFlagsFile);
82 string flags;
83 InputFileStream ifs(filename.c_str());
84 if (ifs) {
85 getline(ifs, flags);
86 }
87 VLOG(1) << "New server flag: " << flags;
88 return flags;
89 }
90 #endif // DEBUG
91 } // namespace
92
93 // initialize default path
ServerLauncher()94 ServerLauncher::ServerLauncher()
95 : server_program_(SystemUtil::GetServerPath()),
96 restricted_(false),
97 suppress_error_dialog_(false) {}
98
~ServerLauncher()99 ServerLauncher::~ServerLauncher() {}
100
StartServer(ClientInterface * client)101 bool ServerLauncher::StartServer(ClientInterface *client) {
102 if (server_program().empty()) {
103 LOG(ERROR) << "Server path is empty";
104 return false;
105 }
106
107 // ping first
108 if (client->PingServer()) {
109 return true;
110 }
111
112 string arg;
113
114 #ifdef OS_WIN
115 // When mozc is not used as a default IME and some applications (like notepad)
116 // are registered in "Start up", mozc_server may not be launched successfully.
117 // This is because the Explorer launches start-up processes inside a group job
118 // and the process inside a job cannot make our sandboxed child processes.
119 // The group job is unregistered after 60 secs (default).
120 //
121 // Here we relax the sandbox restriction if process is in a job.
122 // In order to keep security, the mozc_server is launched
123 // with restricted mode.
124
125 const bool process_in_job = RunLevel::IsProcessInJob();
126 if (process_in_job || restricted_) {
127 LOG(WARNING)
128 << "Parent process is in job. start with restricted mode";
129 arg += "--restricted";
130 }
131 #endif
132
133 #ifdef DEBUG
134 // In oreder to test the Session treatment (timeout/size constratins),
135 // Server flags can be configurable on DEBUG build
136 if (!arg.empty()) {
137 arg += " ";
138 }
139 arg += LoadServerFlags();
140 #endif // DEBUG
141
142 NamedEventListener listener(kServerName);
143 const bool listener_is_available = listener.IsAvailable();
144
145 size_t pid = 0;
146 #ifdef OS_WIN
147 mozc::WinSandbox::SecurityInfo info;
148 // You cannot use WinSandbox::USER_INTERACTIVE here because restricted token
149 // seems to prevent WinHTTP from using SSL. b/5502343
150 info.primary_level = WinSandbox::USER_NON_ADMIN;
151 info.impersonation_level = WinSandbox::USER_RESTRICTED_SAME_ACCESS;
152 info.integrity_level = WinSandbox::INTEGRITY_LEVEL_LOW;
153 // If the current process is in a job, you cannot use
154 // CREATE_BREAKAWAY_FROM_JOB. b/1571395
155 info.use_locked_down_job = !process_in_job;
156 info.allow_ui_operation = false;
157 info.in_system_dir = true; // use system dir not to lock current directory
158 info.creation_flags = CREATE_DEFAULT_ERROR_MODE | CREATE_NO_WINDOW;
159
160 DWORD tmp_pid = 0;
161 const bool result = mozc::WinSandbox::SpawnSandboxedProcess(
162 server_program(), arg, info, &tmp_pid);
163 pid = static_cast<size_t>(tmp_pid);
164
165 if (!result) {
166 LOG(ERROR) << "Can't start process: " << ::GetLastError();
167 return false;
168 }
169 #elif defined(OS_MACOSX)
170 // Use launchd API instead of spawning process. It doesn't use
171 // server_program() at all.
172 const bool result = MacUtil::StartLaunchdService(
173 "Converter", reinterpret_cast<pid_t *>(&pid));
174 if (!result) {
175 LOG(ERROR) << "Can't start process";
176 return false;
177 }
178 #else
179 const bool result = mozc::Process::SpawnProcess(server_program(),
180 arg,
181 &pid);
182 if (!result) {
183 LOG(ERROR) << "Can't start process: " << strerror(result);
184 return false;
185 }
186 #endif // OS_WIN
187
188 // maybe another process will launch mozc_server at the same time.
189 if (client->PingServer()) {
190 VLOG(1) << "Another process has launched the server";
191 return true;
192 }
193
194 // Common part:
195 // Wait until mozc_server becomes ready to process requests
196 if (listener_is_available) {
197 const int ret = listener.WaitEventOrProcess(kServerWaitTimeout, pid);
198 switch (ret) {
199 case NamedEventListener::TIMEOUT:
200 LOG(WARNING) << "seems that " << kProductNameInEnglish << " is not "
201 << "ready within " << kServerWaitTimeout << " msec";
202 break;
203 case NamedEventListener::EVENT_SIGNALED:
204 VLOG(1) << kProductNameInEnglish << " is launched successfully "
205 << "within " << kServerWaitTimeout << " msec";
206 break;
207 case NamedEventListener::PROCESS_SIGNALED:
208 LOG(ERROR) << "Mozc server is terminated";
209 // Mozc may be terminated because another client launches mozc_server
210 if (client->PingServer()) {
211 return true;
212 }
213 return false;
214 }
215 } else {
216 // maybe another process is trying to launch mozc_server.
217 LOG(ERROR) << "cannot make NamedEventListener ";
218 Util::Sleep(kRetryIntervalForServer);
219 }
220
221 // Try to connect mozc_server just in case.
222 for (int trial = 0; trial < kTrial; ++trial) {
223 if (client->PingServer()) {
224 return true;
225 }
226 Util::Sleep(kRetryIntervalForServer);
227 }
228
229 LOG(ERROR) << kProductNameInEnglish << " cannot be launched";
230
231 return false;
232 }
233
ForceTerminateServer(const string & name)234 bool ServerLauncher::ForceTerminateServer(const string &name) {
235 return IPCClient::TerminateServer(name);
236 }
237
WaitServer(uint32 pid)238 bool ServerLauncher::WaitServer(uint32 pid) {
239 const int kTimeout = 10000;
240 return Process::WaitProcess(static_cast<size_t>(pid), kTimeout);
241 }
242
OnFatal(ServerLauncherInterface::ServerErrorType type)243 void ServerLauncher::OnFatal(
244 ServerLauncherInterface::ServerErrorType type) {
245 LOG(ERROR) << "OnFatal is called: " << static_cast<int>(type);
246
247 string error_type;
248 switch (type) {
249 case ServerLauncherInterface::SERVER_TIMEOUT:
250 error_type = "server_timeout";
251 break;
252 case ServerLauncherInterface::SERVER_BROKEN_MESSAGE:
253 error_type = "server_broken_message";
254 break;
255 case ServerLauncherInterface::SERVER_VERSION_MISMATCH:
256 error_type = "server_version_mismatch";
257 break;
258 case ServerLauncherInterface::SERVER_SHUTDOWN:
259 error_type = "server_shutdown";
260 break;
261 case ServerLauncherInterface::SERVER_FATAL:
262 error_type = "server_fatal";
263 break;
264 default:
265 LOG(ERROR) << "Unknown error: " << type;
266 return;
267 }
268
269 if (!suppress_error_dialog_) {
270 Process::LaunchErrorMessageDialog(error_type);
271 }
272 }
273 } // namespace client
274 } // namespace mozc
275