/* * Copyright 2011-2021 Range Networks, Inc. * * This software is distributed under the terms of the GNU Affero Public License. * See the COPYING file in the main directory for details. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ // KEEP THIS FILE CLEAN FOR GPL PUBLIC RELEASE. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define HAVE_LIBREADLINE #ifdef HAVE_LIBREADLINE # include # include #endif // Note that we ONLY use this for the name of the file to use. // The assumption is that OpenBTS and OpenBTSCLI were built together. #include "Globals.h" struct sockaddr_in sa; static char *progname = (char*) ""; char target[64] = "127.0.0.1"; int port = 49300; static void banner() { static int bannerPrinted = false; if (bannerPrinted) return; bannerPrinted = true; printf("OpenBTS Command Line Interface (CLI) utility\n"); printf("Copyright 2012, 2013, 2014 Range Networks, Inc.\n"); printf("Licensed under GPLv2.\n"); #ifdef HAVE_LIBREADLINE printf("Includes libreadline, GPLv2.\n"); #endif } static void oops(const char *fmt, ...) { banner(); va_list ap; va_start(ap,fmt); vprintf(fmt,ap); va_end(ap); printf(" OpenBTSCLI options:\n" " -t ip_address : specify IP address of target machine on which OpenBTS is running\n" " -p port_number : specify OpenBTS port number\n" " -c command .. : execute this OpenBTS command and exit; also suppresses extraneous banners\n" " -d : read one OpenBTS command, execute it and exit\n" "If -d or -c not specified, read OpenBTS commands and execute them.\n" "To see OpenBTS help, type the 'help' command to OpenBTS, or for example: OpenBTSCLI -c help\n" ); exit(1); } bool doCmd(int fd, char *cmd) // return false to abort/exit { const int bufsz = 100000; char resbuf[bufsz]; int nread = 0; int len = strlen(cmd); int svlen = len; len = htonl(len); if (send(fd, &len, sizeof(len), 0) < 0) { perror("sending stream"); return false; } len = svlen; if (send(fd, cmd, strlen(cmd), 0) < 0) { perror("sending stream"); return false; } nread = recv(fd, &len, sizeof(len), 0); if (nread < 0) { perror("receiving stream"); return false; } if (nread == 0) { printf("Remote connection closed\n"); exit(1); } if (nread != (int) sizeof(len)) { printf("Partial read of length from server, expected %d, got %d\n", sizeof(len), len); exit(1); } len = ntohl(len); if (len >= bufsz-1) { printf("Response of %d bytes is too long\n", len); exit(1); } int off = 0; svlen = len; while(len != 0) { nread = recv(fd,&resbuf[off],len,0); if (nread < 0) { perror("receiving stream"); return false; } if (nread == 0) { printf("Remote connection closed\n"); exit(1); } off += nread; len -= nread; } nread = svlen; if (nread<0) { perror("receiving response"); return false; } resbuf[nread] = '\0'; if (strcmp("restart", cmd) == 0) { printf("OpenBTS has been shut down or restarted.\n"); printf("You will need to restart OpenBTSCLI after it restarts.\n"); return false; } if (strcmp("shutdown", cmd) == 0) { printf("OpenBTS has been shut down or restarted.\n"); printf("You will need to restart OpenBTSCLI after it restarts.\n"); return false; } printf("%s\n",resbuf); if (nread==(bufsz-1)) { printf("(response truncated at %d characters)\n",nread); } return true; } int main(int argc, char *argv[]) { bool isBTSDo = false; // If set, execute one command without prompting, then exit. std::string sCommand(""); progname = argv[0]; argc--; argv++; // Skip program name. while(argc > 0) { if (argv[0][0] == '-') { if (strlen(argv[0]) > 2) { oops("Invalid option '%s'\n", argv[0]); exit(1); } switch(argv[0][1]) { case 'd': // OpenBTSDo interface isBTSDo = true; break; case 'c': // Run command on command line then exit. isBTSDo = true; if (argc == 1) { oops("Missing argument to -c\n"); } { // Gather up the command line. for (int j = 1; j < argc; j++) { sCommand += argv[j]; sCommand += " "; } } argc = 1; // terminates while loop. break; case 'p': // TCP Port number argc--, argv++; port = atoi(argv[0]); printf("TCP %d\n", port); break; case 't': // target argc--, argv++; snprintf(target, sizeof(target)-1, "%s", argv[0]); break; default: oops("Invalid option '%s'\n", argv[0]); exit(1); // NOTREACHED but makes the compiler happy. } argc--; argv++; } else { oops("Invalid argument '%s'\n", argv[0]); exit(1); // NOTREACHED but makes the compiler happy. } } if (sCommand.c_str()[0] == '\0') { banner(); printf("Connecting to %s:%d...\n", target, port); } int sock = -1; char prompt[16] = "OpenBTS> "; // Define this stuff "globally" as it's needed in various places memset(&sa, 0, sizeof(sa)); // the socket sock = socket(AF_INET,SOCK_STREAM,0); if (sock<0) { perror("opening stream socket"); exit(1); } // destination address sa.sin_family = AF_INET; sa.sin_port = htons(port); if (inet_pton(AF_INET, target, &sa.sin_addr) <= 0) { oops("unable to convert target to an IP address\n"); } if (0) { // (pat) I used this code for testing. // If you wanted to specify the port you were binding from, this is how you would do it... // We dont use this code - we let the connect system call pick the port. struct sockaddr_in sockAddrBuf; memset(&sockAddrBuf,0,sizeof(sockAddrBuf)); // overkill. sockAddrBuf.sin_family = AF_INET; sockAddrBuf.sin_addr.s_addr = INADDR_ANY; sockAddrBuf.sin_port = htons(13011); if (bind(sock, (struct sockaddr *) &sockAddrBuf, sizeof(struct sockaddr_in))) { // Bind the socket to our assigned port. printf("bind call failed: %s",strerror(errno)); exit(2); } } if (connect(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) { perror("connect stream socket"); fprintf(stderr, "Is OpenBTS running?\n"); exit(1); } #ifdef HAVE_LIBREADLINE char *history_name = 0; if (!isBTSDo) { // start console using_history(); static const char * const history_file_name = "/.openbts_history"; char *home_dir = getenv("HOME"); if(home_dir) { size_t home_dir_len = strlen(home_dir); size_t history_file_len = strlen(history_file_name); size_t history_len = home_dir_len + history_file_len + 1; if(history_len > home_dir_len) { if(!(history_name = (char *)malloc(history_len))) { perror("malloc failed"); exit(2); } memcpy(history_name, home_dir, home_dir_len); memcpy(history_name + home_dir_len, history_file_name, history_file_len + 1); read_history(history_name); } } } #endif if (!isBTSDo) printf("Remote Interface Ready.\nType:\n \"help\" to see commands,\n \"version\" for version information,\n \"notices\" for licensing information,\n \"quit\" to exit console interface.\n"); if (sCommand.c_str()[0] != '\0') { doCmd(sock, (char *)sCommand.c_str()); } else while (1) { #ifdef HAVE_LIBREADLINE char *cmd = readline(isBTSDo ? NULL : prompt); if (!cmd) continue; if (cmd[0] == '\0') continue; if (!isBTSDo) if (*cmd) add_history(cmd); #else // HAVE_LIBREADLINE if (!isBTSDo) { printf("%s",prompt); fflush(stdout); } char *inbuf = (char*)malloc(BUFSIZ); char *cmd = fgets(inbuf,BUFSIZ-1,stdin); if (!cmd) { if (isBTSDo) break; continue; } if (cmd[0] == '\0') continue; // strip trailing CR cmd[strlen(cmd)-1] = '\0'; #endif if (!isBTSDo) { // local quit? if (strcmp(cmd,"quit")==0) { printf("closing remote console\n"); break; } // shutdown via upstart if (strcmp(cmd,"shutdown")==0) { printf("terminating openbts\n"); if (getuid() == 0) system("stop openbts"); else { printf("If prompted, enter the password you use for sudo\n"); system("sudo stop openbts"); } break; } // shell escape? if (cmd[0]=='!') { int i = system(cmd+1); if (i < 0) { perror("system"); } continue; } } char *pCmd = cmd; while(isspace(*pCmd)) pCmd++; // skip leading whitespace if (*pCmd) { if (doCmd(sock, cmd) == false) { bool sd = false; if (strcmp(cmd,"shutdown")==0) sd = true; else if (strcmp(cmd,"restart")==0) sd = true; free(cmd); //{ if (isBTSDo) break; if (sd) break; continue; //} } } free(cmd); if (isBTSDo) break; } #ifdef HAVE_LIBREADLINE if (!isBTSDo) { if(history_name) { int e = write_history(history_name); if(e) { fprintf(stderr, "error: history: %s\n", strerror(e)); } free(history_name); history_name = 0; } } #endif close(sock); }