فهرست منبع

Basic CLI working with JSON output. Now to parse and support old tabular form.

Adam Ierymenko 10 سال پیش
والد
کامیت
8333bf065e
4فایلهای تغییر یافته به همراه232 افزوده شده و 0 حذف شده
  1. 1 0
      make-freebsd.mk
  2. 1 0
      make-linux.mk
  3. 1 0
      make-mac.mk
  4. 229 0
      one.cpp

+ 1 - 0
make-freebsd.mk

@@ -61,6 +61,7 @@ one:	$(OBJS) one.o
 	$(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-one $(OBJS) one.o $(LIBS)
 	$(STRIP) zerotier-one
 	ln -sf zerotier-one zerotier-idtool
+	ln -sf zerotier-one zerotier-cli
 
 selftest:	$(OBJS) selftest.o
 	$(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-selftest selftest.o $(OBJS) $(LIBS)

+ 1 - 0
make-linux.mk

@@ -67,6 +67,7 @@ one:	$(OBJS) one.o
 	$(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-one $(OBJS) one.o $(LIBS)
 	$(STRIP) zerotier-one
 	ln -sf zerotier-one zerotier-idtool
+	ln -sf zerotier-one zerotier-cli
 
 selftest:	$(OBJS) selftest.o
 	$(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-selftest selftest.o $(OBJS) $(LIBS)

+ 1 - 0
make-mac.mk

@@ -56,6 +56,7 @@ one:	$(OBJS) one.o
 	$(CXX) $(CXXFLAGS) -o zerotier-one $(OBJS) one.o $(LIBS)
 	$(STRIP) zerotier-one
 	ln -sf zerotier-one zerotier-idtool
+	ln -sf zerotier-one zerotier-cli
 
 selftest: $(OBJS) selftest.o
 	$(CXX) $(CXXFLAGS) -o zerotier-selftest selftest.o $(OBJS) $(LIBS)

+ 229 - 0
one.cpp

@@ -63,6 +63,7 @@
 #include "node/Utils.hpp"
 #include "node/NetworkController.hpp"
 #include "osdep/OSUtils.hpp"
+#include "osdep/Http.hpp"
 #include "service/OneService.hpp"
 #ifdef ZT_ENABLE_NETWORK_CONTROLLER
 #include "controller/SqliteNetworkController.hpp"
@@ -79,12 +80,229 @@ static OneService *volatile zt1Service = (OneService *)0;
 /* zerotier-cli personality                                                 */
 /****************************************************************************/
 
+static void cliPrintHelp(const char *pn,FILE *out)
+{
+	fprintf(out,"ZeroTier One version %d.%d.%d"ZT_EOL_S"(c)2011-2015 ZeroTier, Inc."ZT_EOL_S,ZEROTIER_ONE_VERSION_MAJOR,ZEROTIER_ONE_VERSION_MINOR,ZEROTIER_ONE_VERSION_REVISION);
+	fprintf(out,"Licensed under the GNU General Public License v3"ZT_EOL_S""ZT_EOL_S);
+	fprintf(out,"Usage: %s [-switches] <command/path> [<args>]"ZT_EOL_S""ZT_EOL_S,pn);
+	fprintf(out,"Available switches:"ZT_EOL_S);
+	fprintf(out,"  -h                 - Display this help"ZT_EOL_S);
+	fprintf(out,"  -v                 - Show version"ZT_EOL_S);
+	fprintf(out,"  -j                 - Display full raw JSON output"ZT_EOL_S);
+	fprintf(out,"  -D<path>           - ZeroTier home path for parameter auto-detect"ZT_EOL_S);
+	fprintf(out,"  -p<port>           - HTTP port (default: auto)"ZT_EOL_S);
+	fprintf(out,"  -T<token>          - Authentication token (default: auto)"ZT_EOL_S);
+	fprintf(out,"  -H<ip>             - HTTP IP address (default: 127.0.0.1)"ZT_EOL_S""ZT_EOL_S);
+	fprintf(out,"Available commands:"ZT_EOL_S);
+	fprintf(out,"  info               - Display status info"ZT_EOL_S);
+	fprintf(out,"  listpeers          - List all peers"ZT_EOL_S);
+	fprintf(out,"  listnetworks       - List all networks"ZT_EOL_S);
+	fprintf(out,"  join <network>     - Join a network"ZT_EOL_S);
+	fprintf(out,"  leave <network>    - Leave a network"ZT_EOL_S);
+}
+
+static std::string cliFixJsonCRs(const std::string &s)
+{
+	std::string r;
+	for(std::string::const_iterator c(s.begin());c!=s.end();++c) {
+		if (*c == '\n')
+			r.append(ZT_EOL_S);
+		else r.push_back(*c);
+	}
+	return r;
+}
+
 #ifdef __WINDOWS__
 static int cli(int argc, _TCHAR* argv[])
 #else
 static int cli(int argc,char **argv)
 #endif
 {
+	unsigned int port = 0;
+	std::string homeDir;
+	std::string command;
+	std::string arg1;
+	std::string authToken;
+	std::string ip("127.0.0.1");
+	bool json = false;
+	for(int i=1;i<argc;++i) {
+		if (argv[i][0] == '-') {
+			switch(argv[i][1]) {
+
+				case 'q': // ignore -q used to invoke this personality
+					if (argv[i][2]) {
+						cliPrintHelp(argv[0],stdout);
+						return 1;
+					}
+					break;
+
+				case 'j':
+					if (argv[i][2]) {
+						cliPrintHelp(argv[0],stdout);
+						return 1;
+					}
+					json = true;
+					break;
+
+				case 'p': // port for HTTP
+					port = Utils::strToUInt(argv[i] + 2);
+					if ((port > 0xffff)||(port == 0)) {
+						cliPrintHelp(argv[0],stdout);
+						return 1;
+					}
+					break;
+
+				case 'D': // Home path
+					if (argv[i][2]) {
+						homeDir = argv[i] + 2;
+					} else {
+						cliPrintHelp(argv[0],stdout);
+						return 1;
+					}
+					break;
+
+				case 'H': // HTTP IP
+					if (argv[i][2]) {
+						ip = argv[i] + 2;
+					} else {
+						cliPrintHelp(argv[0],stdout);
+						return 1;
+					}
+					break;
+
+				case 'T': // Override root topology
+					if (argv[i][2]) {
+						authToken = argv[i] + 2;
+					} else {
+						cliPrintHelp(argv[0],stdout);
+						return 1;
+					}
+					break;
+
+				case 'v': // Display version
+					if (argv[i][2]) {
+						cliPrintHelp(argv[0],stdout);
+						return 1;
+					}
+					printf("%d.%d.%d"ZT_EOL_S,ZEROTIER_ONE_VERSION_MAJOR,ZEROTIER_ONE_VERSION_MINOR,ZEROTIER_ONE_VERSION_REVISION);
+					return 0;
+
+				case 'h':
+				case '?':
+				default:
+					cliPrintHelp(argv[0],stdout);
+					return 0;
+			}
+		} else {
+			if (command.length())
+				arg1 = argv[i];
+			else command = argv[i];
+		}
+	}
+	if (!homeDir.length())
+		homeDir = OneService::platformDefaultHomePath();
+
+	if ((!port)||(!authToken.length())) {
+		if (!homeDir.length()) {
+			fprintf(stderr,"%s: missing port or authentication token and no home directory specified to auto-detect"ZT_EOL_S,argv[0]);
+			return 2;
+		}
+
+		if (!port) {
+			std::string portStr;
+			OSUtils::readFile((homeDir + ZT_PATH_SEPARATOR_S + "zerotier-one.port").c_str(),portStr);
+			port = Utils::strToUInt(portStr.c_str());
+			if ((port == 0)||(port > 0xffff)) {
+				fprintf(stderr,"%s: missing port and zerotier-one.port not found in %s"ZT_EOL_S,argv[0],homeDir.c_str());
+				return 2;
+			}
+		}
+
+		if (!authToken.length()) {
+			OSUtils::readFile((homeDir + ZT_PATH_SEPARATOR_S + "authtoken.secret").c_str(),authToken);
+			if (!authToken.length()) {
+				fprintf(stderr,"%s: missing authentication token and authtoken.secret not found (or readable) in %s"ZT_EOL_S,argv[0],homeDir.c_str());
+				return 2;
+			}
+		}
+	}
+
+	InetAddress addr;
+	{
+		char addrtmp[256];
+		Utils::snprintf(addrtmp,sizeof(addrtmp),"%s/%u",ip.c_str(),port);
+		addr = InetAddress(addrtmp);
+	}
+
+	std::map<std::string,std::string> requestHeaders;
+	std::map<std::string,std::string> responseHeaders;
+	std::string responseBody;
+
+	requestHeaders["X-ZT1-Auth"] = authToken;
+
+	if ((command == "info")||(command == "status")) {
+		unsigned int scode = Http::GET(
+			1024 * 1024 * 16,
+			60000,
+			(const struct sockaddr *)&addr,
+			"/status",
+			requestHeaders,
+			responseHeaders,
+			responseBody);
+		if (scode == 200) {
+			if (json) {
+				printf("%s",cliFixJsonCRs(responseBody).c_str());
+				return 0;
+			} else {
+			}
+		} else {
+			printf("%u %s %s"ZT_EOL_S,scode,command.c_str(),responseBody.c_str());
+			return 1;
+		}
+	} else if (command == "listpeers") {
+		unsigned int scode = Http::GET(
+			1024 * 1024 * 16,
+			60000,
+			(const struct sockaddr *)&addr,
+			"/peer",
+			requestHeaders,
+			responseHeaders,
+			responseBody);
+		if (scode == 200) {
+			if (json) {
+				printf("%s",cliFixJsonCRs(responseBody).c_str());
+				return 0;
+			} else {
+			}
+		} else {
+			printf("%u %s %s"ZT_EOL_S,scode,command.c_str(),responseBody.c_str());
+			return 1;
+		}
+	} else if (command == "listnetworks") {
+		unsigned int scode = Http::GET(
+			1024 * 1024 * 16,
+			60000,
+			(const struct sockaddr *)&addr,
+			"/network",
+			requestHeaders,
+			responseHeaders,
+			responseBody);
+		if (scode == 200) {
+			if (json) {
+				printf("%s",cliFixJsonCRs(responseBody).c_str());
+				return 0;
+			} else {
+			}
+		} else {
+			printf("%u %s %s"ZT_EOL_S,scode,command.c_str(),responseBody.c_str());
+			return 1;
+		}
+	} else if (command == "join") {
+	} else if (command == "leave") {
+	} else {
+		cliPrintHelp(argv[0],stderr);
+		return 0;
+	}
 }
 
 /****************************************************************************/
@@ -93,6 +311,8 @@ static int cli(int argc,char **argv)
 
 static void idtoolPrintHelp(FILE *out,const char *pn)
 {
+	fprintf(out,"ZeroTier One version %d.%d.%d"ZT_EOL_S"(c)2011-2015 ZeroTier, Inc."ZT_EOL_S,ZEROTIER_ONE_VERSION_MAJOR,ZEROTIER_ONE_VERSION_MINOR,ZEROTIER_ONE_VERSION_REVISION);
+	fprintf(out,"Licensed under the GNU General Public License v3"ZT_EOL_S""ZT_EOL_S);
 	fprintf(out,"Usage: %s <command> [<args>]"ZT_EOL_S""ZT_EOL_S"Commands:"ZT_EOL_S,pn);
 	fprintf(out,"  generate [<identity.secret>] [<identity.public>]"ZT_EOL_S);
 	fprintf(out,"  validate <identity.secret/public>"ZT_EOL_S);
@@ -445,6 +665,7 @@ static void printHelp(const char *cn,FILE *out)
 	fprintf(out,"  -d                - Fork and run as daemon (Unix-ish OSes)"ZT_EOL_S);
 #endif // __UNIX_LIKE__
 	fprintf(out,"  -i                - Generate and manage identities (zerotier-idtool)"ZT_EOL_S);
+	fprintf(out,"  -q                - Query API (zerotier-cli)"ZT_EOL_S);
 #ifdef __WINDOWS__
 	fprintf(out,"  -C                - Run from command line instead of as service (Windows)"ZT_EOL_S);
 	fprintf(out,"  -I                - Install Windows service (Windows)"ZT_EOL_S);
@@ -496,6 +717,8 @@ int main(int argc,char **argv)
 
 	if ((strstr(argv[0],"zerotier-idtool"))||(strstr(argv[0],"ZEROTIER-IDTOOL")))
 		return idtool(argc,argv);
+	if ((strstr(argv[0],"zerotier-cli"))||(strstr(argv[0],"ZEROTIER-CLI")))
+		return cli(argc,argv);
 
 	std::string overrideRootTopology;
 	std::string homeDir;
@@ -541,6 +764,12 @@ int main(int argc,char **argv)
 						return 0;
 					} else return idtool(argc,argv);
 
+				case 'q': // Invoke cli personality
+					if (argv[i][2]) {
+						printHelp(argv[0],stdout);
+						return 0;
+					} else return cli(argc,argv);
+
 #ifdef __WINDOWS__
 				case 'C': // Run from command line instead of as Windows service
 					winRunFromCommandLine = true;