node_state_json.cpp 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. #include "version.h"
  2. #include "diagnostic/node_state_json.hpp"
  3. #include "diagnostic/node_state_sections.hpp"
  4. #include "diagnostic/node_state_interfaces_linux.hpp" // platform-specific, add others as needed
  5. #include <nlohmann/json.hpp>
  6. #include <ctime>
  7. #include <iomanip>
  8. #include <sstream>
  9. #include <fstream>
  10. #include <iostream>
  11. #include <cstdio>
  12. #include <unistd.h>
  13. #include <sys/types.h>
  14. #include <sys/stat.h>
  15. #include <sys/utsname.h>
  16. namespace {
  17. std::string make_timestamp() {
  18. auto t = std::time(nullptr);
  19. std::tm tm_utc = *std::gmtime(&t);
  20. char buf[32];
  21. std::strftime(buf, sizeof(buf), "%Y%m%dT%H%M%SZ", &tm_utc);
  22. return std::string(buf);
  23. }
  24. }
  25. void write_node_state_json(const ZeroTier::InetAddress &addr, const std::string &homeDir, std::map<std::string, std::string> &requestHeaders, std::map<std::string, std::string> &responseHeaders, std::string &responseBody) {
  26. nlohmann::json j;
  27. // Schema version for MCP/diagnostic output
  28. j["schema_version"] = "1.0"; // Update this if the schema changes
  29. std::vector<std::string> errors;
  30. // Timestamps
  31. auto t = std::time(nullptr);
  32. auto tm_utc = *std::gmtime(&t);
  33. auto tm_local = *std::localtime(&t);
  34. std::stringstream utc_ts, local_ts;
  35. utc_ts << std::put_time(&tm_utc, "%Y-%m-%dT%H:%M:%SZ");
  36. local_ts << std::put_time(&tm_local, "%Y-%m-%dT%H:%M:%S%z");
  37. j["utc_timestamp"] = utc_ts.str();
  38. j["local_timestamp"] = local_ts.str();
  39. #ifdef __APPLE__
  40. j["platform"] = "macOS";
  41. #elif defined(_WIN32)
  42. j["platform"] = "Windows";
  43. #elif defined(__linux__)
  44. j["platform"] = "Linux";
  45. #else
  46. j["platform"] = "other unix based OS";
  47. #endif
  48. j["zerotier_version"] = std::to_string(ZEROTIER_ONE_VERSION_MAJOR) + "." + std::to_string(ZEROTIER_ONE_VERSION_MINOR) + "." + std::to_string(ZEROTIER_ONE_VERSION_REVISION);
  49. // Extensibility/context fields
  50. // node_role: placeholder (could be "controller", "member", etc.)
  51. j["node_role"] = nullptr; // Set to actual role if available
  52. // uptime: seconds since boot (best effort)
  53. long uptime = -1;
  54. #ifdef __linux__
  55. FILE* f = fopen("/proc/uptime", "r");
  56. if (f) {
  57. if (fscanf(f, "%ld", &uptime) != 1) uptime = -1;
  58. fclose(f);
  59. }
  60. #endif
  61. if (uptime >= 0)
  62. j["uptime"] = uptime;
  63. else
  64. j["uptime"] = nullptr;
  65. // hostname
  66. char hostname[256] = {};
  67. if (gethostname(hostname, sizeof(hostname)) == 0) {
  68. j["hostname"] = hostname;
  69. } else {
  70. j["hostname"] = nullptr;
  71. }
  72. // tags: extensibility array for future use (e.g., MCP tags, custom info)
  73. j["tags"] = nlohmann::json::array();
  74. // mcp_context: extensibility object for MCP or plugin context
  75. j["mcp_context"] = nlohmann::json::object();
  76. // Add each section
  77. try {
  78. addNodeStateStatusJson(j, addr, requestHeaders);
  79. } catch (const std::exception& e) {
  80. errors.push_back(std::string("status section: ") + e.what());
  81. } catch (...) {
  82. errors.push_back("status section: unknown error");
  83. }
  84. try {
  85. addNodeStateNetworksJson(j, addr, requestHeaders);
  86. } catch (const std::exception& e) {
  87. errors.push_back(std::string("networks section: ") + e.what());
  88. } catch (...) {
  89. errors.push_back("networks section: unknown error");
  90. }
  91. try {
  92. addNodeStatePeersJson(j, addr, requestHeaders);
  93. } catch (const std::exception& e) {
  94. errors.push_back(std::string("peers section: ") + e.what());
  95. } catch (...) {
  96. errors.push_back("peers section: unknown error");
  97. }
  98. try {
  99. addNodeStateLocalConfJson(j, homeDir);
  100. } catch (const std::exception& e) {
  101. errors.push_back(std::string("local_conf section: ") + e.what());
  102. } catch (...) {
  103. errors.push_back("local_conf section: unknown error");
  104. }
  105. try {
  106. addNodeStateInterfacesJson(j); // platform-specific
  107. } catch (const std::exception& e) {
  108. errors.push_back(std::string("interfaces section: ") + e.what());
  109. } catch (...) {
  110. errors.push_back("interfaces section: unknown error");
  111. }
  112. j["errors"] = errors;
  113. // Filename: nodeId and timestamp
  114. std::string nodeId = (j.contains("nodeId") && j["nodeId"].is_string()) ? j["nodeId"].get<std::string>() : "unknown";
  115. std::string timestamp = make_timestamp();
  116. std::string filename = "zerotier_node_state_" + nodeId + "_" + timestamp + ".json";
  117. std::string tmp_path = "/tmp/" + filename;
  118. std::string cwd_path = filename;
  119. std::string json_str = j.dump(2);
  120. // Try /tmp, then cwd, then stdout
  121. bool written = false;
  122. {
  123. std::ofstream ofs(tmp_path);
  124. if (ofs) {
  125. ofs << json_str;
  126. ofs.close();
  127. std::cout << "Wrote node state to: " << tmp_path << std::endl;
  128. written = true;
  129. }
  130. }
  131. if (!written) {
  132. std::ofstream ofs(cwd_path);
  133. if (ofs) {
  134. ofs << json_str;
  135. ofs.close();
  136. std::cout << "Wrote node state to: " << cwd_path << std::endl;
  137. written = true;
  138. }
  139. }
  140. if (!written) {
  141. std::cout << json_str << std::endl;
  142. std::cerr << "Could not write node state to file, output to stdout instead." << std::endl;
  143. }
  144. }