cmServer.cxx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #include "cmServer.h"
  4. #include "cmConnection.h"
  5. #include "cmFileMonitor.h"
  6. #include "cmServerDictionary.h"
  7. #include "cmServerProtocol.h"
  8. #include "cmSystemTools.h"
  9. #include "cm_jsoncpp_reader.h"
  10. #include "cm_jsoncpp_writer.h"
  11. #include "cmake.h"
  12. #include "cmsys/FStream.hxx"
  13. #include <algorithm>
  14. #include <cassert>
  15. #include <cstdint>
  16. #include <memory>
  17. #include <utility>
  18. void on_signal(uv_signal_t* signal, int signum)
  19. {
  20. auto conn = reinterpret_cast<cmServerBase*>(signal->data);
  21. conn->OnSignal(signum);
  22. }
  23. static void on_walk_to_shutdown(uv_handle_t* handle, void* arg)
  24. {
  25. (void)arg;
  26. if (!uv_is_closing(handle)) {
  27. uv_close(handle, &cmConnection::on_close);
  28. }
  29. }
  30. class cmServer::DebugInfo
  31. {
  32. public:
  33. DebugInfo()
  34. : StartTime(uv_hrtime())
  35. {
  36. }
  37. bool PrintStatistics = false;
  38. std::string OutputFile;
  39. uint64_t StartTime;
  40. };
  41. cmServer::cmServer(cmConnection* conn, bool supportExperimental)
  42. : cmServerBase(conn)
  43. , SupportExperimental(supportExperimental)
  44. {
  45. // Register supported protocols:
  46. this->RegisterProtocol(new cmServerProtocol1_0);
  47. }
  48. cmServer::~cmServer()
  49. {
  50. if (!this->Protocol) { // Server was never fully started!
  51. return;
  52. }
  53. for (cmServerProtocol* p : this->SupportedProtocols) {
  54. delete p;
  55. }
  56. }
  57. void cmServer::ProcessRequest(cmConnection* connection,
  58. const std::string& input)
  59. {
  60. Json::Reader reader;
  61. Json::Value value;
  62. if (!reader.parse(input, value)) {
  63. this->WriteParseError(connection, "Failed to parse JSON input.");
  64. return;
  65. }
  66. std::unique_ptr<DebugInfo> debug;
  67. Json::Value debugValue = value["debug"];
  68. if (!debugValue.isNull()) {
  69. debug = std::make_unique<DebugInfo>();
  70. debug->OutputFile = debugValue["dumpToFile"].asString();
  71. debug->PrintStatistics = debugValue["showStats"].asBool();
  72. }
  73. const cmServerRequest request(this, value[kTYPE_KEY].asString(),
  74. value[kCOOKIE_KEY].asString(), value);
  75. if (request.Type == "") {
  76. cmServerResponse response(request);
  77. response.SetError("No type given in request.");
  78. this->WriteResponse(connection, response, nullptr);
  79. return;
  80. }
  81. cmSystemTools::SetMessageCallback(reportMessage,
  82. const_cast<cmServerRequest*>(&request));
  83. if (this->Protocol) {
  84. this->Protocol->CMakeInstance()->SetProgressCallback(
  85. reportProgress, const_cast<cmServerRequest*>(&request));
  86. this->WriteResponse(connection, this->Protocol->Process(request),
  87. debug.get());
  88. } else {
  89. this->WriteResponse(connection, this->SetProtocolVersion(request),
  90. debug.get());
  91. }
  92. }
  93. void cmServer::RegisterProtocol(cmServerProtocol* protocol)
  94. {
  95. if (protocol->IsExperimental() && !this->SupportExperimental) {
  96. return;
  97. }
  98. auto version = protocol->ProtocolVersion();
  99. assert(version.first >= 0);
  100. assert(version.second >= 0);
  101. auto it = std::find_if(this->SupportedProtocols.begin(),
  102. this->SupportedProtocols.end(),
  103. [version](cmServerProtocol* p) {
  104. return p->ProtocolVersion() == version;
  105. });
  106. if (it == this->SupportedProtocols.end()) {
  107. this->SupportedProtocols.push_back(protocol);
  108. }
  109. }
  110. void cmServer::PrintHello(cmConnection* connection) const
  111. {
  112. Json::Value hello = Json::objectValue;
  113. hello[kTYPE_KEY] = "hello";
  114. Json::Value& protocolVersions = hello[kSUPPORTED_PROTOCOL_VERSIONS] =
  115. Json::arrayValue;
  116. for (auto const& proto : this->SupportedProtocols) {
  117. auto version = proto->ProtocolVersion();
  118. Json::Value tmp = Json::objectValue;
  119. tmp[kMAJOR_KEY] = version.first;
  120. tmp[kMINOR_KEY] = version.second;
  121. if (proto->IsExperimental()) {
  122. tmp[kIS_EXPERIMENTAL_KEY] = true;
  123. }
  124. protocolVersions.append(tmp);
  125. }
  126. this->WriteJsonObject(connection, hello, nullptr);
  127. }
  128. void cmServer::reportProgress(const char* msg, float progress, void* data)
  129. {
  130. const cmServerRequest* request = static_cast<const cmServerRequest*>(data);
  131. assert(request);
  132. if (progress < 0.0f || progress > 1.0f) {
  133. request->ReportMessage(msg, "");
  134. } else {
  135. request->ReportProgress(0, static_cast<int>(progress * 1000), 1000, msg);
  136. }
  137. }
  138. void cmServer::reportMessage(const char* msg, const char* title,
  139. bool& /* cancel */, void* data)
  140. {
  141. const cmServerRequest* request = static_cast<const cmServerRequest*>(data);
  142. assert(request);
  143. assert(msg);
  144. std::string titleString;
  145. if (title) {
  146. titleString = title;
  147. }
  148. request->ReportMessage(std::string(msg), titleString);
  149. }
  150. cmServerResponse cmServer::SetProtocolVersion(const cmServerRequest& request)
  151. {
  152. if (request.Type != kHANDSHAKE_TYPE) {
  153. return request.ReportError("Waiting for type \"" + kHANDSHAKE_TYPE +
  154. "\".");
  155. }
  156. Json::Value requestedProtocolVersion = request.Data[kPROTOCOL_VERSION_KEY];
  157. if (requestedProtocolVersion.isNull()) {
  158. return request.ReportError("\"" + kPROTOCOL_VERSION_KEY +
  159. "\" is required for \"" + kHANDSHAKE_TYPE +
  160. "\".");
  161. }
  162. if (!requestedProtocolVersion.isObject()) {
  163. return request.ReportError("\"" + kPROTOCOL_VERSION_KEY +
  164. "\" must be a JSON object.");
  165. }
  166. Json::Value majorValue = requestedProtocolVersion[kMAJOR_KEY];
  167. if (!majorValue.isInt()) {
  168. return request.ReportError("\"" + kMAJOR_KEY +
  169. "\" must be set and an integer.");
  170. }
  171. Json::Value minorValue = requestedProtocolVersion[kMINOR_KEY];
  172. if (!minorValue.isNull() && !minorValue.isInt()) {
  173. return request.ReportError("\"" + kMINOR_KEY +
  174. "\" must be unset or an integer.");
  175. }
  176. const int major = majorValue.asInt();
  177. const int minor = minorValue.isNull() ? -1 : minorValue.asInt();
  178. if (major < 0) {
  179. return request.ReportError("\"" + kMAJOR_KEY + "\" must be >= 0.");
  180. }
  181. if (!minorValue.isNull() && minor < 0) {
  182. return request.ReportError("\"" + kMINOR_KEY +
  183. "\" must be >= 0 when set.");
  184. }
  185. this->Protocol =
  186. this->FindMatchingProtocol(this->SupportedProtocols, major, minor);
  187. if (!this->Protocol) {
  188. return request.ReportError("Protocol version not supported.");
  189. }
  190. std::string errorMessage;
  191. if (!this->Protocol->Activate(this, request, &errorMessage)) {
  192. this->Protocol = CM_NULLPTR;
  193. return request.ReportError("Failed to activate protocol version: " +
  194. errorMessage);
  195. }
  196. return request.Reply(Json::objectValue);
  197. }
  198. bool cmServer::Serve(std::string* errorMessage)
  199. {
  200. if (this->SupportedProtocols.empty()) {
  201. *errorMessage =
  202. "No protocol versions defined. Maybe you need --experimental?";
  203. return false;
  204. }
  205. assert(!this->Protocol);
  206. return cmServerBase::Serve(errorMessage);
  207. }
  208. cmFileMonitor* cmServer::FileMonitor() const
  209. {
  210. return fileMonitor.get();
  211. }
  212. void cmServer::WriteJsonObject(const Json::Value& jsonValue,
  213. const DebugInfo* debug) const
  214. {
  215. for (auto& connection : this->Connections) {
  216. WriteJsonObject(connection.get(), jsonValue, debug);
  217. }
  218. }
  219. void cmServer::WriteJsonObject(cmConnection* connection,
  220. const Json::Value& jsonValue,
  221. const DebugInfo* debug) const
  222. {
  223. Json::FastWriter writer;
  224. auto beforeJson = uv_hrtime();
  225. std::string result = writer.write(jsonValue);
  226. if (debug) {
  227. Json::Value copy = jsonValue;
  228. if (debug->PrintStatistics) {
  229. Json::Value stats = Json::objectValue;
  230. auto endTime = uv_hrtime();
  231. stats["jsonSerialization"] = double(endTime - beforeJson) / 1000000.0;
  232. stats["totalTime"] = double(endTime - debug->StartTime) / 1000000.0;
  233. stats["size"] = static_cast<int>(result.size());
  234. if (!debug->OutputFile.empty()) {
  235. stats["dumpFile"] = debug->OutputFile;
  236. }
  237. copy["zzzDebug"] = stats;
  238. result = writer.write(copy); // Update result to include debug info
  239. }
  240. if (!debug->OutputFile.empty()) {
  241. cmsys::ofstream myfile(debug->OutputFile.c_str());
  242. myfile << result;
  243. }
  244. }
  245. connection->WriteData(std::string("\n") + kSTART_MAGIC + std::string("\n") +
  246. result + kEND_MAGIC + std::string("\n"));
  247. }
  248. cmServerProtocol* cmServer::FindMatchingProtocol(
  249. const std::vector<cmServerProtocol*>& protocols, int major, int minor)
  250. {
  251. cmServerProtocol* bestMatch = nullptr;
  252. for (auto protocol : protocols) {
  253. auto version = protocol->ProtocolVersion();
  254. if (major != version.first) {
  255. continue;
  256. }
  257. if (minor == version.second) {
  258. return protocol;
  259. }
  260. if (!bestMatch || bestMatch->ProtocolVersion().second < version.second) {
  261. bestMatch = protocol;
  262. }
  263. }
  264. return minor < 0 ? bestMatch : nullptr;
  265. }
  266. void cmServer::WriteProgress(const cmServerRequest& request, int min,
  267. int current, int max,
  268. const std::string& message) const
  269. {
  270. assert(min <= current && current <= max);
  271. assert(message.length() != 0);
  272. Json::Value obj = Json::objectValue;
  273. obj[kTYPE_KEY] = kPROGRESS_TYPE;
  274. obj[kREPLY_TO_KEY] = request.Type;
  275. obj[kCOOKIE_KEY] = request.Cookie;
  276. obj[kPROGRESS_MESSAGE_KEY] = message;
  277. obj[kPROGRESS_MINIMUM_KEY] = min;
  278. obj[kPROGRESS_MAXIMUM_KEY] = max;
  279. obj[kPROGRESS_CURRENT_KEY] = current;
  280. this->WriteJsonObject(obj, nullptr);
  281. }
  282. void cmServer::WriteMessage(const cmServerRequest& request,
  283. const std::string& message,
  284. const std::string& title) const
  285. {
  286. if (message.empty()) {
  287. return;
  288. }
  289. Json::Value obj = Json::objectValue;
  290. obj[kTYPE_KEY] = kMESSAGE_TYPE;
  291. obj[kREPLY_TO_KEY] = request.Type;
  292. obj[kCOOKIE_KEY] = request.Cookie;
  293. obj[kMESSAGE_KEY] = message;
  294. if (!title.empty()) {
  295. obj[kTITLE_KEY] = title;
  296. }
  297. WriteJsonObject(obj, nullptr);
  298. }
  299. void cmServer::WriteParseError(cmConnection* connection,
  300. const std::string& message) const
  301. {
  302. Json::Value obj = Json::objectValue;
  303. obj[kTYPE_KEY] = kERROR_TYPE;
  304. obj[kERROR_MESSAGE_KEY] = message;
  305. obj[kREPLY_TO_KEY] = "";
  306. obj[kCOOKIE_KEY] = "";
  307. this->WriteJsonObject(connection, obj, nullptr);
  308. }
  309. void cmServer::WriteSignal(const std::string& name,
  310. const Json::Value& data) const
  311. {
  312. assert(data.isObject());
  313. Json::Value obj = data;
  314. obj[kTYPE_KEY] = kSIGNAL_TYPE;
  315. obj[kREPLY_TO_KEY] = "";
  316. obj[kCOOKIE_KEY] = "";
  317. obj[kNAME_KEY] = name;
  318. WriteJsonObject(obj, nullptr);
  319. }
  320. void cmServer::WriteResponse(cmConnection* connection,
  321. const cmServerResponse& response,
  322. const DebugInfo* debug) const
  323. {
  324. assert(response.IsComplete());
  325. Json::Value obj = response.Data();
  326. obj[kCOOKIE_KEY] = response.Cookie;
  327. obj[kTYPE_KEY] = response.IsError() ? kERROR_TYPE : kREPLY_TYPE;
  328. obj[kREPLY_TO_KEY] = response.Type;
  329. if (response.IsError()) {
  330. obj[kERROR_MESSAGE_KEY] = response.ErrorMessage();
  331. }
  332. this->WriteJsonObject(connection, obj, debug);
  333. }
  334. void cmServer::OnConnected(cmConnection* connection)
  335. {
  336. PrintHello(connection);
  337. }
  338. void cmServer::OnServeStart()
  339. {
  340. cmServerBase::OnServeStart();
  341. fileMonitor = std::make_shared<cmFileMonitor>(GetLoop());
  342. }
  343. void cmServer::StartShutDown()
  344. {
  345. if (fileMonitor) {
  346. fileMonitor->StopMonitoring();
  347. fileMonitor.reset();
  348. }
  349. cmServerBase::StartShutDown();
  350. }
  351. static void __start_thread(void* arg)
  352. {
  353. auto server = reinterpret_cast<cmServerBase*>(arg);
  354. std::string error;
  355. server->Serve(&error);
  356. }
  357. bool cmServerBase::StartServeThread()
  358. {
  359. ServeThreadRunning = true;
  360. uv_thread_create(&ServeThread, __start_thread, this);
  361. return true;
  362. }
  363. bool cmServerBase::Serve(std::string* errorMessage)
  364. {
  365. errorMessage->clear();
  366. uv_signal_init(&Loop, &this->SIGINTHandler);
  367. uv_signal_init(&Loop, &this->SIGHUPHandler);
  368. this->SIGINTHandler.data = this;
  369. this->SIGHUPHandler.data = this;
  370. uv_signal_start(&this->SIGINTHandler, &on_signal, SIGINT);
  371. uv_signal_start(&this->SIGHUPHandler, &on_signal, SIGHUP);
  372. OnServeStart();
  373. for (auto& connection : Connections) {
  374. if (!connection->OnServeStart(errorMessage)) {
  375. return false;
  376. }
  377. }
  378. if (uv_run(&Loop, UV_RUN_DEFAULT) != 0) {
  379. *errorMessage = "Internal Error: Event loop stopped in unclean state.";
  380. StartShutDown();
  381. return false;
  382. }
  383. ServeThreadRunning = false;
  384. return true;
  385. }
  386. void cmServerBase::OnConnected(cmConnection*)
  387. {
  388. }
  389. void cmServerBase::OnDisconnect()
  390. {
  391. }
  392. void cmServerBase::OnServeStart()
  393. {
  394. uv_signal_start(&this->SIGINTHandler, &on_signal, SIGINT);
  395. uv_signal_start(&this->SIGHUPHandler, &on_signal, SIGHUP);
  396. }
  397. void cmServerBase::StartShutDown()
  398. {
  399. if (!uv_is_closing((const uv_handle_t*)&this->SIGINTHandler)) {
  400. uv_signal_stop(&this->SIGINTHandler);
  401. }
  402. if (!uv_is_closing((const uv_handle_t*)&this->SIGHUPHandler)) {
  403. uv_signal_stop(&this->SIGHUPHandler);
  404. }
  405. for (auto& connection : Connections) {
  406. connection->OnServerShuttingDown();
  407. }
  408. Connections.clear();
  409. uv_stop(&Loop);
  410. uv_walk(&Loop, on_walk_to_shutdown, CM_NULLPTR);
  411. uv_run(&Loop, UV_RUN_DEFAULT);
  412. }
  413. bool cmServerBase::OnSignal(int signum)
  414. {
  415. (void)signum;
  416. StartShutDown();
  417. return true;
  418. }
  419. cmServerBase::cmServerBase(cmConnection* connection)
  420. {
  421. uv_loop_init(&Loop);
  422. uv_signal_init(&Loop, &this->SIGINTHandler);
  423. uv_signal_init(&Loop, &this->SIGHUPHandler);
  424. this->SIGINTHandler.data = this;
  425. this->SIGHUPHandler.data = this;
  426. AddNewConnection(connection);
  427. }
  428. cmServerBase::~cmServerBase()
  429. {
  430. if (ServeThreadRunning) {
  431. StartShutDown();
  432. uv_thread_join(&ServeThread);
  433. }
  434. uv_loop_close(&Loop);
  435. }
  436. void cmServerBase::AddNewConnection(cmConnection* ownedConnection)
  437. {
  438. Connections.emplace_back(ownedConnection);
  439. ownedConnection->SetServer(this);
  440. }
  441. uv_loop_t* cmServerBase::GetLoop()
  442. {
  443. return &Loop;
  444. }
  445. void cmServerBase::OnDisconnect(cmConnection* pConnection)
  446. {
  447. auto pred = [pConnection](const std::unique_ptr<cmConnection>& m) {
  448. return m.get() == pConnection;
  449. };
  450. Connections.erase(
  451. std::remove_if(Connections.begin(), Connections.end(), pred),
  452. Connections.end());
  453. if (Connections.empty()) {
  454. StartShutDown();
  455. }
  456. }