| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 // | 4 // |
| 5 // This file implements a standalone host process for Me2Me. | 5 // This file implements a standalone host process for Me2Me. |
| 6 | 6 |
| 7 #include <string> | 7 #include <string> |
| 8 | 8 |
| 9 #include "base/at_exit.h" | 9 #include "base/at_exit.h" |
| 10 #include "base/bind.h" | 10 #include "base/bind.h" |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 57 #include "remoting/host/ipc_desktop_environment.h" | 57 #include "remoting/host/ipc_desktop_environment.h" |
| 58 #include "remoting/host/ipc_host_event_logger.h" | 58 #include "remoting/host/ipc_host_event_logger.h" |
| 59 #include "remoting/host/logging.h" | 59 #include "remoting/host/logging.h" |
| 60 #include "remoting/host/me2me_desktop_environment.h" | 60 #include "remoting/host/me2me_desktop_environment.h" |
| 61 #include "remoting/host/pairing_registry_delegate.h" | 61 #include "remoting/host/pairing_registry_delegate.h" |
| 62 #include "remoting/host/policy_watcher.h" | 62 #include "remoting/host/policy_watcher.h" |
| 63 #include "remoting/host/session_manager_factory.h" | 63 #include "remoting/host/session_manager_factory.h" |
| 64 #include "remoting/host/shutdown_watchdog.h" | 64 #include "remoting/host/shutdown_watchdog.h" |
| 65 #include "remoting/host/signaling_connector.h" | 65 #include "remoting/host/signaling_connector.h" |
| 66 #include "remoting/host/single_window_desktop_environment.h" | 66 #include "remoting/host/single_window_desktop_environment.h" |
| 67 #include "remoting/host/third_party_auth_config.h" |
| 67 #include "remoting/host/token_validator_factory_impl.h" | 68 #include "remoting/host/token_validator_factory_impl.h" |
| 68 #include "remoting/host/usage_stats_consent.h" | 69 #include "remoting/host/usage_stats_consent.h" |
| 69 #include "remoting/host/username.h" | 70 #include "remoting/host/username.h" |
| 70 #include "remoting/host/video_frame_recorder_host_extension.h" | 71 #include "remoting/host/video_frame_recorder_host_extension.h" |
| 71 #include "remoting/protocol/me2me_host_authenticator_factory.h" | 72 #include "remoting/protocol/me2me_host_authenticator_factory.h" |
| 72 #include "remoting/protocol/network_settings.h" | 73 #include "remoting/protocol/network_settings.h" |
| 73 #include "remoting/protocol/pairing_registry.h" | 74 #include "remoting/protocol/pairing_registry.h" |
| 75 #include "remoting/protocol/port_range.h" |
| 74 #include "remoting/protocol/token_validator.h" | 76 #include "remoting/protocol/token_validator.h" |
| 75 #include "remoting/signaling/xmpp_signal_strategy.h" | 77 #include "remoting/signaling/xmpp_signal_strategy.h" |
| 76 | 78 |
| 77 #if defined(OS_POSIX) | 79 #if defined(OS_POSIX) |
| 78 #include <signal.h> | 80 #include <signal.h> |
| 79 #include <sys/types.h> | 81 #include <sys/types.h> |
| 80 #include <unistd.h> | 82 #include <unistd.h> |
| 81 #include "base/file_descriptor_posix.h" | 83 #include "base/file_descriptor_posix.h" |
| 82 #include "remoting/host/pam_authorization_factory_posix.h" | 84 #include "remoting/host/pam_authorization_factory_posix.h" |
| 83 #include "remoting/host/posix/signal_handler.h" | 85 #include "remoting/host/posix/signal_handler.h" |
| (...skipping 248 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 332 bool use_service_account_; | 334 bool use_service_account_; |
| 333 bool enable_vp9_; | 335 bool enable_vp9_; |
| 334 int64_t frame_recorder_buffer_size_; | 336 int64_t frame_recorder_buffer_size_; |
| 335 | 337 |
| 336 scoped_ptr<PolicyWatcher> policy_watcher_; | 338 scoped_ptr<PolicyWatcher> policy_watcher_; |
| 337 PolicyState policy_state_; | 339 PolicyState policy_state_; |
| 338 std::string host_domain_; | 340 std::string host_domain_; |
| 339 bool host_username_match_required_; | 341 bool host_username_match_required_; |
| 340 bool allow_nat_traversal_; | 342 bool allow_nat_traversal_; |
| 341 bool allow_relay_; | 343 bool allow_relay_; |
| 342 uint16 min_udp_port_; | 344 PortRange udp_port_range_; |
| 343 uint16 max_udp_port_; | |
| 344 std::string talkgadget_prefix_; | 345 std::string talkgadget_prefix_; |
| 345 bool allow_pairing_; | 346 bool allow_pairing_; |
| 346 | 347 |
| 347 bool curtain_required_; | 348 bool curtain_required_; |
| 348 ThirdPartyAuthConfig third_party_auth_config_; | 349 ThirdPartyAuthConfig third_party_auth_config_; |
| 349 bool enable_gnubby_auth_; | 350 bool enable_gnubby_auth_; |
| 350 | 351 |
| 351 // Boolean to change flow, where necessary, if we're | 352 // Boolean to change flow, where necessary, if we're |
| 352 // capturing a window instead of the entire desktop. | 353 // capturing a window instead of the entire desktop. |
| 353 bool enable_window_capture_; | 354 bool enable_window_capture_; |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 387 ShutdownWatchdog* shutdown_watchdog) | 388 ShutdownWatchdog* shutdown_watchdog) |
| 388 : context_(context.Pass()), | 389 : context_(context.Pass()), |
| 389 state_(HOST_STARTING), | 390 state_(HOST_STARTING), |
| 390 use_service_account_(false), | 391 use_service_account_(false), |
| 391 enable_vp9_(false), | 392 enable_vp9_(false), |
| 392 frame_recorder_buffer_size_(0), | 393 frame_recorder_buffer_size_(0), |
| 393 policy_state_(POLICY_INITIALIZING), | 394 policy_state_(POLICY_INITIALIZING), |
| 394 host_username_match_required_(false), | 395 host_username_match_required_(false), |
| 395 allow_nat_traversal_(true), | 396 allow_nat_traversal_(true), |
| 396 allow_relay_(true), | 397 allow_relay_(true), |
| 397 min_udp_port_(0), | |
| 398 max_udp_port_(0), | |
| 399 allow_pairing_(true), | 398 allow_pairing_(true), |
| 400 curtain_required_(false), | 399 curtain_required_(false), |
| 401 enable_gnubby_auth_(false), | 400 enable_gnubby_auth_(false), |
| 402 enable_window_capture_(false), | 401 enable_window_capture_(false), |
| 403 window_id_(0), | 402 window_id_(0), |
| 404 #if defined(REMOTING_MULTI_PROCESS) | 403 #if defined(REMOTING_MULTI_PROCESS) |
| 405 desktop_session_connector_(nullptr), | 404 desktop_session_connector_(nullptr), |
| 406 #endif // defined(REMOTING_MULTI_PROCESS) | 405 #endif // defined(REMOTING_MULTI_PROCESS) |
| 407 self_(this), | 406 self_(this), |
| 408 exit_code_out_(exit_code_out), | 407 exit_code_out_(exit_code_out), |
| (...skipping 256 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 665 | 664 |
| 666 std::string local_certificate = key_pair_->GenerateCertificate(); | 665 std::string local_certificate = key_pair_->GenerateCertificate(); |
| 667 if (local_certificate.empty()) { | 666 if (local_certificate.empty()) { |
| 668 LOG(ERROR) << "Failed to generate host certificate."; | 667 LOG(ERROR) << "Failed to generate host certificate."; |
| 669 ShutdownHost(kInitializationFailed); | 668 ShutdownHost(kInitializationFailed); |
| 670 return; | 669 return; |
| 671 } | 670 } |
| 672 | 671 |
| 673 scoped_ptr<protocol::AuthenticatorFactory> factory; | 672 scoped_ptr<protocol::AuthenticatorFactory> factory; |
| 674 | 673 |
| 675 if (third_party_auth_config_.is_empty()) { | 674 if (third_party_auth_config_.is_null()) { |
| 676 scoped_refptr<PairingRegistry> pairing_registry; | 675 scoped_refptr<PairingRegistry> pairing_registry; |
| 677 if (allow_pairing_) { | 676 if (allow_pairing_) { |
| 678 // On Windows |pairing_registry_| is initialized in | 677 // On Windows |pairing_registry_| is initialized in |
| 679 // InitializePairingRegistry(). | 678 // InitializePairingRegistry(). |
| 680 #if !defined(OS_WIN) | 679 #if !defined(OS_WIN) |
| 681 if (!pairing_registry_) { | 680 if (!pairing_registry_) { |
| 682 scoped_ptr<PairingRegistry::Delegate> delegate = | 681 scoped_ptr<PairingRegistry::Delegate> delegate = |
| 683 CreatePairingRegistryDelegate(); | 682 CreatePairingRegistryDelegate(); |
| 684 | 683 |
| 685 if (delegate) | 684 if (delegate) |
| 686 pairing_registry_ = new PairingRegistry(context_->file_task_runner(), | 685 pairing_registry_ = new PairingRegistry(context_->file_task_runner(), |
| 687 delegate.Pass()); | 686 delegate.Pass()); |
| 688 } | 687 } |
| 689 #endif // defined(OS_WIN) | 688 #endif // defined(OS_WIN) |
| 690 | 689 |
| 691 pairing_registry = pairing_registry_; | 690 pairing_registry = pairing_registry_; |
| 692 } | 691 } |
| 693 | 692 |
| 694 factory = protocol::Me2MeHostAuthenticatorFactory::CreateWithSharedSecret( | 693 factory = protocol::Me2MeHostAuthenticatorFactory::CreateWithSharedSecret( |
| 695 use_service_account_, host_owner_, local_certificate, key_pair_, | 694 use_service_account_, host_owner_, local_certificate, key_pair_, |
| 696 host_secret_hash_, pairing_registry); | 695 host_secret_hash_, pairing_registry); |
| 697 | 696 |
| 698 host_->set_pairing_registry(pairing_registry); | 697 host_->set_pairing_registry(pairing_registry); |
| 699 } else if (third_party_auth_config_.is_valid()) { | 698 } else { |
| 699 DCHECK(third_party_auth_config_.token_url.is_valid()); |
| 700 DCHECK(third_party_auth_config_.token_validation_url.is_valid()); |
| 701 |
| 700 scoped_ptr<protocol::TokenValidatorFactory> token_validator_factory( | 702 scoped_ptr<protocol::TokenValidatorFactory> token_validator_factory( |
| 701 new TokenValidatorFactoryImpl( | 703 new TokenValidatorFactoryImpl( |
| 702 third_party_auth_config_, | 704 third_party_auth_config_, |
| 703 key_pair_, context_->url_request_context_getter())); | 705 key_pair_, context_->url_request_context_getter())); |
| 704 factory = protocol::Me2MeHostAuthenticatorFactory::CreateWithThirdPartyAuth( | 706 factory = protocol::Me2MeHostAuthenticatorFactory::CreateWithThirdPartyAuth( |
| 705 use_service_account_, host_owner_, local_certificate, key_pair_, | 707 use_service_account_, host_owner_, local_certificate, key_pair_, |
| 706 token_validator_factory.Pass()); | 708 token_validator_factory.Pass()); |
| 707 | |
| 708 } else { | |
| 709 // TODO(rmsousa): If the policy is bad the host should not go online. It | |
| 710 // should keep running, but not connected, until the policies are fixed. | |
| 711 // Having it show up as online and then reject all clients is misleading. | |
| 712 LOG(ERROR) << "One of the third-party token URLs is empty or invalid. " | |
| 713 << "Host will reject all clients until policies are corrected. " | |
| 714 << "TokenUrl: " << third_party_auth_config_.token_url << ", " | |
| 715 << "TokenValidationUrl: " | |
| 716 << third_party_auth_config_.token_validation_url; | |
| 717 factory = protocol::Me2MeHostAuthenticatorFactory::CreateRejecting(); | |
| 718 } | 709 } |
| 719 | 710 |
| 720 #if defined(OS_POSIX) | 711 #if defined(OS_POSIX) |
| 721 // On Linux and Mac, perform a PAM authorization step after authentication. | 712 // On Linux and Mac, perform a PAM authorization step after authentication. |
| 722 factory.reset(new PamAuthorizationFactory(factory.Pass())); | 713 factory.reset(new PamAuthorizationFactory(factory.Pass())); |
| 723 #endif | 714 #endif |
| 724 host_->SetAuthenticatorFactory(factory.Pass()); | 715 host_->SetAuthenticatorFactory(factory.Pass()); |
| 725 } | 716 } |
| 726 | 717 |
| 727 // IPC::Listener implementation. | 718 // IPC::Listener implementation. |
| (...skipping 470 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1198 } else { | 1189 } else { |
| 1199 HOST_LOG << "Policy disables use of relay server."; | 1190 HOST_LOG << "Policy disables use of relay server."; |
| 1200 } | 1191 } |
| 1201 return true; | 1192 return true; |
| 1202 } | 1193 } |
| 1203 | 1194 |
| 1204 bool HostProcess::OnUdpPortPolicyUpdate(base::DictionaryValue* policies) { | 1195 bool HostProcess::OnUdpPortPolicyUpdate(base::DictionaryValue* policies) { |
| 1205 // Returns true if the host has to be restarted after this policy update. | 1196 // Returns true if the host has to be restarted after this policy update. |
| 1206 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); | 1197 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); |
| 1207 | 1198 |
| 1208 std::string udp_port_range; | 1199 std::string string_value; |
| 1209 if (!policies->GetString(policy::key::kRemoteAccessHostUdpPortRange, | 1200 if (!policies->GetString(policy::key::kRemoteAccessHostUdpPortRange, |
| 1210 &udp_port_range)) { | 1201 &string_value)) { |
| 1211 return false; | 1202 return false; |
| 1212 } | 1203 } |
| 1213 | 1204 |
| 1214 // Use default values if policy setting is empty or invalid. | 1205 DCHECK(PortRange::Parse(string_value, &udp_port_range_)); |
| 1215 uint16 min_udp_port = 0; | 1206 HOST_LOG << "Policy restricts UDP port range to: " << udp_port_range_; |
| 1216 uint16 max_udp_port = 0; | 1207 return true; |
| 1217 if (!udp_port_range.empty() && | |
| 1218 !NetworkSettings::ParsePortRange(udp_port_range, &min_udp_port, | |
| 1219 &max_udp_port)) { | |
| 1220 LOG(WARNING) << "Invalid port range policy: \"" << udp_port_range | |
| 1221 << "\". Using default values."; | |
| 1222 } | |
| 1223 | |
| 1224 if (min_udp_port_ != min_udp_port || max_udp_port_ != max_udp_port) { | |
| 1225 if (min_udp_port != 0 && max_udp_port != 0) { | |
| 1226 HOST_LOG << "Policy restricts UDP port range to [" << min_udp_port | |
| 1227 << ", " << max_udp_port << "]"; | |
| 1228 } else { | |
| 1229 HOST_LOG << "Policy does not restrict UDP port range."; | |
| 1230 } | |
| 1231 min_udp_port_ = min_udp_port; | |
| 1232 max_udp_port_ = max_udp_port; | |
| 1233 return true; | |
| 1234 } | |
| 1235 return false; | |
| 1236 } | 1208 } |
| 1237 | 1209 |
| 1238 bool HostProcess::OnCurtainPolicyUpdate(base::DictionaryValue* policies) { | 1210 bool HostProcess::OnCurtainPolicyUpdate(base::DictionaryValue* policies) { |
| 1239 // Returns true if the host has to be restarted after this policy update. | 1211 // Returns true if the host has to be restarted after this policy update. |
| 1240 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); | 1212 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); |
| 1241 | 1213 |
| 1242 if (!policies->GetBoolean(policy::key::kRemoteAccessHostRequireCurtain, | 1214 if (!policies->GetBoolean(policy::key::kRemoteAccessHostRequireCurtain, |
| 1243 &curtain_required_)) { | 1215 &curtain_required_)) { |
| 1244 return false; | 1216 return false; |
| 1245 } | 1217 } |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1283 if (!policies->GetString(policy::key::kRemoteAccessHostTalkGadgetPrefix, | 1255 if (!policies->GetString(policy::key::kRemoteAccessHostTalkGadgetPrefix, |
| 1284 &talkgadget_prefix_)) { | 1256 &talkgadget_prefix_)) { |
| 1285 return false; | 1257 return false; |
| 1286 } | 1258 } |
| 1287 | 1259 |
| 1288 HOST_LOG << "Policy sets talkgadget prefix: " << talkgadget_prefix_; | 1260 HOST_LOG << "Policy sets talkgadget prefix: " << talkgadget_prefix_; |
| 1289 return true; | 1261 return true; |
| 1290 } | 1262 } |
| 1291 | 1263 |
| 1292 bool HostProcess::OnHostTokenUrlPolicyUpdate(base::DictionaryValue* policies) { | 1264 bool HostProcess::OnHostTokenUrlPolicyUpdate(base::DictionaryValue* policies) { |
| 1293 // Returns true if the host has to be restarted after this policy update. | 1265 switch (ThirdPartyAuthConfig::Parse(*policies, &third_party_auth_config_)) { |
| 1294 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); | 1266 case ThirdPartyAuthConfig::NoPolicy: |
| 1295 | 1267 return false; |
| 1296 bool token_policy_changed = false; | 1268 case ThirdPartyAuthConfig::ParsingSuccess: |
| 1297 std::string token_url_string; | 1269 HOST_LOG << "Policy sets third-party token URLs: " |
| 1298 if (policies->GetString(policy::key::kRemoteAccessHostTokenUrl, | 1270 << third_party_auth_config_; |
| 1299 &token_url_string)) { | 1271 return true; |
| 1300 token_policy_changed = true; | 1272 case ThirdPartyAuthConfig::InvalidPolicy: |
| 1301 third_party_auth_config_.token_url = GURL(token_url_string); | 1273 default: |
| 1274 NOTREACHED(); |
| 1275 return false; |
| 1302 } | 1276 } |
| 1303 std::string token_validation_url_string; | |
| 1304 if (policies->GetString(policy::key::kRemoteAccessHostTokenValidationUrl, | |
| 1305 &token_validation_url_string)) { | |
| 1306 token_policy_changed = true; | |
| 1307 third_party_auth_config_.token_validation_url = | |
| 1308 GURL(token_validation_url_string); | |
| 1309 } | |
| 1310 if (policies->GetString( | |
| 1311 policy::key::kRemoteAccessHostTokenValidationCertificateIssuer, | |
| 1312 &third_party_auth_config_.token_validation_cert_issuer)) { | |
| 1313 token_policy_changed = true; | |
| 1314 } | |
| 1315 | |
| 1316 if (token_policy_changed) { | |
| 1317 HOST_LOG << "Policy sets third-party token URLs: " | |
| 1318 << "TokenUrl: " | |
| 1319 << third_party_auth_config_.token_url << ", " | |
| 1320 << "TokenValidationUrl: " | |
| 1321 << third_party_auth_config_.token_validation_url << ", " | |
| 1322 << "TokenValidationCertificateIssuer: " | |
| 1323 << third_party_auth_config_.token_validation_cert_issuer; | |
| 1324 } | |
| 1325 return token_policy_changed; | |
| 1326 } | 1277 } |
| 1327 | 1278 |
| 1328 bool HostProcess::OnPairingPolicyUpdate(base::DictionaryValue* policies) { | 1279 bool HostProcess::OnPairingPolicyUpdate(base::DictionaryValue* policies) { |
| 1329 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); | 1280 DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); |
| 1330 | 1281 |
| 1331 if (!policies->GetBoolean(policy::key::kRemoteAccessHostAllowClientPairing, | 1282 if (!policies->GetBoolean(policy::key::kRemoteAccessHostAllowClientPairing, |
| 1332 &allow_pairing_)) { | 1283 &allow_pairing_)) { |
| 1333 return false; | 1284 return false; |
| 1334 } | 1285 } |
| 1335 | 1286 |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1401 uint32 network_flags = 0; | 1352 uint32 network_flags = 0; |
| 1402 if (allow_nat_traversal_) { | 1353 if (allow_nat_traversal_) { |
| 1403 network_flags = NetworkSettings::NAT_TRAVERSAL_STUN | | 1354 network_flags = NetworkSettings::NAT_TRAVERSAL_STUN | |
| 1404 NetworkSettings::NAT_TRAVERSAL_OUTGOING; | 1355 NetworkSettings::NAT_TRAVERSAL_OUTGOING; |
| 1405 if (allow_relay_) | 1356 if (allow_relay_) |
| 1406 network_flags |= NetworkSettings::NAT_TRAVERSAL_RELAY; | 1357 network_flags |= NetworkSettings::NAT_TRAVERSAL_RELAY; |
| 1407 } | 1358 } |
| 1408 | 1359 |
| 1409 NetworkSettings network_settings(network_flags); | 1360 NetworkSettings network_settings(network_flags); |
| 1410 | 1361 |
| 1411 if (min_udp_port_ && max_udp_port_) { | 1362 if (!udp_port_range_.is_null()) { |
| 1412 network_settings.min_port = min_udp_port_; | 1363 network_settings.port_range = udp_port_range_; |
| 1413 network_settings.max_port = max_udp_port_; | |
| 1414 } else if (!allow_nat_traversal_) { | 1364 } else if (!allow_nat_traversal_) { |
| 1415 // For legacy reasons we have to restrict the port range to a set of default | 1365 // For legacy reasons we have to restrict the port range to a set of default |
| 1416 // values when nat traversal is disabled, even if the port range was not | 1366 // values when nat traversal is disabled, even if the port range was not |
| 1417 // set in policy. | 1367 // set in policy. |
| 1418 network_settings.min_port = NetworkSettings::kDefaultMinPort; | 1368 network_settings.port_range.min_port = NetworkSettings::kDefaultMinPort; |
| 1419 network_settings.max_port = NetworkSettings::kDefaultMaxPort; | 1369 network_settings.port_range.max_port = NetworkSettings::kDefaultMaxPort; |
| 1420 } | 1370 } |
| 1421 | 1371 |
| 1422 host_.reset(new ChromotingHost( | 1372 host_.reset(new ChromotingHost( |
| 1423 host_signaling_manager_->signal_strategy(), | 1373 host_signaling_manager_->signal_strategy(), |
| 1424 desktop_environment_factory_.get(), | 1374 desktop_environment_factory_.get(), |
| 1425 CreateHostSessionManager(host_signaling_manager_->signal_strategy(), | 1375 CreateHostSessionManager(host_signaling_manager_->signal_strategy(), |
| 1426 network_settings, | 1376 network_settings, |
| 1427 context_->url_request_context_getter()), | 1377 context_->url_request_context_getter()), |
| 1428 context_->audio_task_runner(), context_->input_task_runner(), | 1378 context_->audio_task_runner(), context_->input_task_runner(), |
| 1429 context_->video_capture_task_runner(), | 1379 context_->video_capture_task_runner(), |
| (...skipping 196 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1626 base::TimeDelta::FromSeconds(kShutdownTimeoutSeconds)); | 1576 base::TimeDelta::FromSeconds(kShutdownTimeoutSeconds)); |
| 1627 new HostProcess(context.Pass(), &exit_code, &shutdown_watchdog); | 1577 new HostProcess(context.Pass(), &exit_code, &shutdown_watchdog); |
| 1628 | 1578 |
| 1629 // Run the main (also UI) message loop until the host no longer needs it. | 1579 // Run the main (also UI) message loop until the host no longer needs it. |
| 1630 message_loop.Run(); | 1580 message_loop.Run(); |
| 1631 | 1581 |
| 1632 return exit_code; | 1582 return exit_code; |
| 1633 } | 1583 } |
| 1634 | 1584 |
| 1635 } // namespace remoting | 1585 } // namespace remoting |
| OLD | NEW |