process_proxy_registry.cc 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  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. #include "chromeos/process_proxy/process_proxy_registry.h"
  5. #include <memory>
  6. #include "base/bind.h"
  7. #include "base/command_line.h"
  8. #include "base/message_loop/message_pump_type.h"
  9. #include "base/strings/string_number_conversions.h"
  10. #include "base/task/lazy_thread_pool_task_runner.h"
  11. #include "base/task/sequenced_task_runner.h"
  12. namespace chromeos {
  13. namespace {
  14. const char kWatcherThreadName[] = "ProcessWatcherThread";
  15. const char kStdoutOutputType[] = "stdout";
  16. const char kExitOutputType[] = "exit";
  17. const char* ProcessOutputTypeToString(ProcessOutputType type) {
  18. switch (type) {
  19. case PROCESS_OUTPUT_TYPE_OUT:
  20. return kStdoutOutputType;
  21. case PROCESS_OUTPUT_TYPE_EXIT:
  22. return kExitOutputType;
  23. default:
  24. return NULL;
  25. }
  26. }
  27. // This instance must be leaked because the destructor would be run on the main
  28. // thread, and not the task runner.
  29. static base::LazyInstance<ProcessProxyRegistry>::Leaky
  30. g_process_proxy_registry = LAZY_INSTANCE_INITIALIZER;
  31. } // namespace
  32. ProcessProxyRegistry::ProcessProxyInfo::ProcessProxyInfo() = default;
  33. ProcessProxyRegistry::ProcessProxyInfo::ProcessProxyInfo(
  34. const ProcessProxyInfo& other) {
  35. // This should be called with empty info only.
  36. DCHECK(!other.proxy.get());
  37. }
  38. ProcessProxyRegistry::ProcessProxyInfo::~ProcessProxyInfo() = default;
  39. ProcessProxyRegistry::ProcessProxyRegistry() = default;
  40. ProcessProxyRegistry::~ProcessProxyRegistry() {
  41. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  42. ShutDown();
  43. }
  44. void ProcessProxyRegistry::ShutDown() {
  45. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  46. // Close all proxies we own.
  47. while (!proxy_map_.empty())
  48. CloseProcess(proxy_map_.begin()->first);
  49. if (watcher_thread_) {
  50. watcher_thread_->Stop();
  51. watcher_thread_.reset();
  52. }
  53. }
  54. // static
  55. ProcessProxyRegistry* ProcessProxyRegistry::Get() {
  56. DCHECK(ProcessProxyRegistry::GetTaskRunner()->RunsTasksInCurrentSequence());
  57. return g_process_proxy_registry.Pointer();
  58. }
  59. // static
  60. int ProcessProxyRegistry::ConvertToSystemPID(const std::string& id) {
  61. // The `id` is <pid>-<guid>. `base::StringToInt()` will parse until the '-'.
  62. int out;
  63. base::StringToInt(id, &out);
  64. return out;
  65. }
  66. // static
  67. scoped_refptr<base::SequencedTaskRunner> ProcessProxyRegistry::GetTaskRunner() {
  68. static base::LazyThreadPoolSequencedTaskRunner task_runner =
  69. LAZY_THREAD_POOL_SEQUENCED_TASK_RUNNER_INITIALIZER(
  70. base::TaskTraits(base::MayBlock(), base::TaskPriority::BEST_EFFORT));
  71. return task_runner.Get();
  72. }
  73. bool ProcessProxyRegistry::OpenProcess(const base::CommandLine& cmdline,
  74. const std::string& user_id_hash,
  75. const OutputCallback& output_callback,
  76. std::string* id) {
  77. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  78. if (!EnsureWatcherThreadStarted())
  79. return false;
  80. // Create and open new proxy.
  81. scoped_refptr<ProcessProxy> proxy(new ProcessProxy());
  82. if (!proxy->Open(cmdline, user_id_hash, id))
  83. return false;
  84. // Kick off watcher.
  85. // We can use Unretained because proxy will stop calling callback after it is
  86. // closed, which is done before this object goes away.
  87. if (!proxy->StartWatchingOutput(
  88. watcher_thread_->task_runner(), GetTaskRunner(),
  89. base::BindRepeating(&ProcessProxyRegistry::OnProcessOutput,
  90. base::Unretained(this), *id))) {
  91. proxy->Close();
  92. return false;
  93. }
  94. ProcessProxyInfo& info = proxy_map_[*id];
  95. info.proxy.swap(proxy);
  96. info.callback = output_callback;
  97. return true;
  98. }
  99. void ProcessProxyRegistry::SendInput(const std::string& id,
  100. const std::string& data,
  101. base::OnceCallback<void(bool)> callback) {
  102. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  103. std::map<std::string, ProcessProxyInfo>::iterator it = proxy_map_.find(id);
  104. if (it == proxy_map_.end())
  105. return std::move(callback).Run(false);
  106. it->second.proxy->Write(data, std::move(callback));
  107. }
  108. bool ProcessProxyRegistry::CloseProcess(const std::string& id) {
  109. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  110. std::map<std::string, ProcessProxyInfo>::iterator it = proxy_map_.find(id);
  111. if (it == proxy_map_.end())
  112. return false;
  113. it->second.proxy->Close();
  114. proxy_map_.erase(it);
  115. return true;
  116. }
  117. bool ProcessProxyRegistry::OnTerminalResize(const std::string& id,
  118. int width,
  119. int height) {
  120. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  121. std::map<std::string, ProcessProxyInfo>::iterator it = proxy_map_.find(id);
  122. if (it == proxy_map_.end())
  123. return false;
  124. return it->second.proxy->OnTerminalResize(width, height);
  125. }
  126. void ProcessProxyRegistry::AckOutput(const std::string& id) {
  127. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  128. std::map<std::string, ProcessProxyInfo>::iterator it = proxy_map_.find(id);
  129. if (it == proxy_map_.end())
  130. return;
  131. it->second.proxy->AckOutput();
  132. }
  133. void ProcessProxyRegistry::OnProcessOutput(const std::string& id,
  134. ProcessOutputType type,
  135. const std::string& data) {
  136. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  137. const char* type_str = ProcessOutputTypeToString(type);
  138. DCHECK(type_str);
  139. std::map<std::string, ProcessProxyInfo>::iterator it = proxy_map_.find(id);
  140. if (it == proxy_map_.end())
  141. return;
  142. it->second.callback.Run(id, std::string(type_str), data);
  143. // Contact with the slave end of the terminal has been lost. We have to close
  144. // the process.
  145. if (type == PROCESS_OUTPUT_TYPE_EXIT)
  146. CloseProcess(id);
  147. }
  148. bool ProcessProxyRegistry::EnsureWatcherThreadStarted() {
  149. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  150. if (watcher_thread_.get())
  151. return true;
  152. // TODO(tbarzic): Change process output watcher to watch for fd readability on
  153. // FILE thread, and move output reading to worker thread instead of
  154. // spinning a new thread.
  155. watcher_thread_ = std::make_unique<base::Thread>(kWatcherThreadName);
  156. return watcher_thread_->StartWithOptions(
  157. base::Thread::Options(base::MessagePumpType::IO, 0));
  158. }
  159. const base::Process* ProcessProxyRegistry::GetProcessForTesting(
  160. const std::string& id) {
  161. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  162. std::map<std::string, ProcessProxyInfo>::iterator it = proxy_map_.find(id);
  163. if (it == proxy_map_.end())
  164. return nullptr;
  165. return it->second.proxy->GetProcessForTesting(); // IN-TEST
  166. }
  167. } // namespace chromeos