frp.cpp 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. //
  2. // Created by xcbosa on 2023/1/31.
  3. //
  4. #pragma once
  5. #include <string>
  6. #include "utils/utils.h"
  7. #include "webuiconf.h"
  8. #include "fs.hpp"
  9. #include <signal.h>
  10. #include <strstream>
  11. using namespace std;
  12. using namespace xc;
  13. using namespace xc::utils;
  14. namespace frp {
  15. set<int> profileUsingPorts(string profile) {
  16. set<int> usingPorts;
  17. INIFile ini(conf::getFrpcDir() + "/" + profile);
  18. string value = ini.getMust("common")->get("webui_allowServerPorts");
  19. auto v = split(value, ",");
  20. int low(0), len(0);
  21. if (v.size() == 2) {
  22. try {
  23. low = stoi(v[0]);
  24. len = stoi(v[1]);
  25. } catch (...) { }
  26. }
  27. for (int i = low; i < low + len; i++) {
  28. usingPorts.insert(i);
  29. }
  30. return usingPorts;
  31. }
  32. set<int> serverUsingPorts(string serverIp) {
  33. set<int> usingPorts;
  34. for (string file : fs::contentsOfDirectory(conf::getFrpcDir())) {
  35. INIFile ini(conf::getFrpcDir() + "/" + file);
  36. if (ini.getMust("common")->get("server_addr") == serverIp) {
  37. auto profilePorts = profileUsingPorts(file);
  38. for (auto port : profilePorts) {
  39. usingPorts.insert(port);
  40. }
  41. }
  42. }
  43. return usingPorts;
  44. }
  45. void addProfile(string name, string ip, string port, string token) {
  46. INI ini;
  47. auto frpCommon = ini.getMust("common");
  48. frpCommon->set("server_addr", ip);
  49. frpCommon->set("server_port", port);
  50. frpCommon->set("token", token);
  51. frpCommon->set("tls_enable", "true");
  52. auto alreadyUsingPorts = serverUsingPorts(ip);
  53. int low = 0, len = conf::allowPortCountPerProfile;
  54. for (int i = 10000; i < 60000; i += len) {
  55. bool badPage = false;
  56. for (int j = i; j < i + len; j++) {
  57. if (alreadyUsingPorts.count(j)) {
  58. badPage = true;
  59. break;
  60. }
  61. }
  62. if (!badPage) {
  63. low = i;
  64. break;
  65. }
  66. }
  67. if (low == 0) {
  68. len = 0;
  69. }
  70. ostringstream oss;
  71. oss << low;
  72. oss << ",";
  73. oss << len;
  74. frpCommon->set("webui_allowServerPorts", oss.str());
  75. saveTextFile(conf::getFrpcDir() + "/" + name, ini.getINIString());
  76. }
  77. class FrpProcessWrapper {
  78. public:
  79. FrpProcessWrapper(string fileName, string filePath) {
  80. this->fileName = fileName;
  81. this->filePath = filePath;
  82. this->updated = false;
  83. }
  84. void startAndKeepRunning() {
  85. cout << "[FrpProcessWrapper] [" << fileName << "] Start" << endl;
  86. this->doKill();
  87. this->doStart();
  88. }
  89. void update() {
  90. if (getRunningPid() == 0) {
  91. cout << "[FrpProcessWrapper] [" << fileName << "] Exit unexpectedly, restarting..." << endl;
  92. this->doStart();
  93. }
  94. }
  95. int getRunningPid() {
  96. ::FILE *psStdoutFd = popen("ps -ef", "r");
  97. ostringstream oss;
  98. while (true) {
  99. int ch = ::fgetc(psStdoutFd);
  100. if (ch == -1) break;
  101. oss << (char) ch;
  102. }
  103. ::pclose(psStdoutFd);
  104. string str = oss.str();
  105. auto lines = split(str, "\n");
  106. for (auto line : lines) {
  107. if (line.find("frpc -c") != line.npos && line.find(this->filePath) != line.npos) {
  108. strstream ss;
  109. ss << line;
  110. string owner;
  111. int pid;
  112. ss >> owner;
  113. ss >> pid;
  114. return pid;
  115. }
  116. }
  117. return 0;
  118. }
  119. void stop() {
  120. cout << "[FrpProcessWrapper] [" << fileName << "] Stop" << endl;
  121. this->doKill();
  122. }
  123. void reloadConfig() {
  124. this->stop();
  125. this->startAndKeepRunning();
  126. }
  127. bool updated;
  128. string fileName;
  129. string filePath;
  130. private:
  131. void doStart() {
  132. ostringstream oss;
  133. oss << "frpc -c " << this->filePath << " &";
  134. string launchCmd = oss.str();
  135. ::system(launchCmd.c_str());
  136. }
  137. void doKill() {
  138. int pid = getRunningPid();
  139. if (pid > 0) {
  140. kill(pid, SIGKILL);
  141. }
  142. }
  143. };
  144. set<string> reloadConfigForFilePathRequests;
  145. mutex reloadConfigForFilePathRequestsLocker;
  146. void reloadProfileFilePath(string filePath) {
  147. reloadConfigForFilePathRequestsLocker.lock();
  148. reloadConfigForFilePathRequests.insert(filePath);
  149. reloadConfigForFilePathRequestsLocker.unlock();
  150. }
  151. void frpDaemon() {
  152. char readBuff[1024];
  153. vector<FrpProcessWrapper *> frpProcesses;
  154. while (true) {
  155. for (auto it : frpProcesses) {
  156. it->updated = false;
  157. }
  158. for (string file : fs::contentsOfDirectory(conf::getFrpcDir())) {
  159. string filePath = conf::getFrpcDir() + "/" + file;
  160. bool updated = false;
  161. for (auto process : frpProcesses) {
  162. if (process->filePath == filePath) {
  163. process->update();
  164. process->updated = true;
  165. updated = true;
  166. break;
  167. }
  168. }
  169. if (!updated) {
  170. auto newProcess = new FrpProcessWrapper(file, filePath);
  171. newProcess->startAndKeepRunning();
  172. newProcess->updated = true;
  173. frpProcesses.push_back(newProcess);
  174. }
  175. }
  176. for (auto it = frpProcesses.begin(); it != frpProcesses.end(); ) {
  177. FrpProcessWrapper *proc = *it;
  178. if (!proc->updated) {
  179. proc->stop();
  180. delete proc;
  181. it = frpProcesses.erase(it);
  182. } else {
  183. reloadConfigForFilePathRequestsLocker.lock();
  184. if (reloadConfigForFilePathRequests.count(proc->filePath)) {
  185. reloadConfigForFilePathRequests.erase(proc->filePath);
  186. reloadConfigForFilePathRequestsLocker.unlock();
  187. proc->reloadConfig();
  188. } else {
  189. reloadConfigForFilePathRequestsLocker.unlock();
  190. }
  191. it++;
  192. }
  193. }
  194. usleep(1000 * 1000);
  195. }
  196. }
  197. }