cmServer.cxx 15 KB

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