Browse Source

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

Adam Ierymenko 10 years ago
parent
commit
8333bf065e
4 changed files with 232 additions and 0 deletions
  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;