mirror of
https://github.com/fairwaves/openbts-2.8.git
synced 2025-10-22 23:32:00 +00:00
New-style socketed CLI support. Hooray histories!
git-svn-id: http://wush.net/svn/range/software/public/openbts/trunk@3690 19bc5d8c-e614-43d4-8b26-e1612bc8e597
This commit is contained in:
@@ -25,7 +25,9 @@ AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
|
||||
AM_CXXFLAGS = -Wall -ldl -pthread
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
OpenBTS
|
||||
OpenBTS \
|
||||
OpenBTSDo \
|
||||
OpenBTSCLI
|
||||
|
||||
OpenBTS_SOURCES = OpenBTS.cpp
|
||||
OpenBTS_LDADD = \
|
||||
@@ -42,6 +44,10 @@ OpenBTS_LDADD = \
|
||||
$(OSIP_LIBS) \
|
||||
$(ORTP_LIBS)
|
||||
|
||||
OpenBTSCLI_SOURCES = OpenBTSCLI.cpp
|
||||
OpenBTSDo_SOURCES = OpenBTSDo.cpp
|
||||
OpenBTSCLI_LDADD = -lreadline -lncurses
|
||||
|
||||
EXTRA_DIST = \
|
||||
OpenBTS.example.sql
|
||||
|
||||
|
112
apps/OpenBTS.cpp
112
apps/OpenBTS.cpp
@@ -58,12 +58,6 @@ ConfigurationTable gConfig("/etc/OpenBTS/OpenBTS.db");
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
#ifdef HAVE_LIBREADLINE // [
|
||||
//# include <stdio.h>
|
||||
# include <readline/readline.h>
|
||||
# include <readline/history.h>
|
||||
#endif // HAVE_LIBREADLINE ]
|
||||
|
||||
using namespace std;
|
||||
using namespace GSM;
|
||||
|
||||
@@ -139,6 +133,12 @@ void startTransceiver()
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
|
||||
int sock = socket(AF_UNIX,SOCK_DGRAM,0);
|
||||
if (sock<0) {
|
||||
perror("opening cmd datagram socket");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
srandom(time(NULL));
|
||||
@@ -314,85 +314,51 @@ int main(int argc, char *argv[])
|
||||
// OK, now it is safe to start the BTS.
|
||||
gBTS.start();
|
||||
|
||||
#ifdef HAVE_LIBREADLINE // [
|
||||
// start console
|
||||
using_history();
|
||||
|
||||
static const char * const history_file_name = "/.openbts_history";
|
||||
char *history_name = 0;
|
||||
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))) {
|
||||
LOG(ERR) << "malloc failed: " << strerror(errno);
|
||||
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 // HAVE_LIBREADLINE ]
|
||||
|
||||
|
||||
|
||||
cout << "\nsystem ready\n";
|
||||
cout << "\nuse the OpenBTSCLI utility to access CLI\n";
|
||||
LOG(INFO) << "system ready";
|
||||
COUT("\n\nWelcome to OpenBTS. Type \"help\" to see available commands.");
|
||||
// FIXME: We want to catch control-d (emacs keybinding for exit())
|
||||
|
||||
struct sockaddr_un cmdSockName;
|
||||
cmdSockName.sun_family = AF_UNIX;
|
||||
const char* sockpath = gConfig.getStr("CLI.SocketPath","/var/run/OpenBTS/command").c_str();
|
||||
char rmcmd[strlen(sockpath)+5];
|
||||
sprintf(rmcmd,"rm %s",sockpath);
|
||||
system(rmcmd);
|
||||
strcpy(cmdSockName.sun_path,sockpath);
|
||||
if (bind(sock, (struct sockaddr *) &cmdSockName, sizeof(struct sockaddr_un))) {
|
||||
perror("binding name to cmd datagram socket");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// The logging parts were removed from this loop.
|
||||
// If we want them back, they will need to go into their own thread.
|
||||
while (1) {
|
||||
#ifdef HAVE_LIBREADLINE // [
|
||||
char *inbuf = readline(gConfig.getStr("CLI.Prompt").c_str());
|
||||
if (!inbuf) break;
|
||||
if (*inbuf) {
|
||||
add_history(inbuf);
|
||||
// The parser returns -1 on exit.
|
||||
if (gParser.process(inbuf, cout, cin)<0) {
|
||||
free(inbuf);
|
||||
break;
|
||||
}
|
||||
char cmdbuf[1000];
|
||||
struct sockaddr_un source;
|
||||
socklen_t sourceSize = sizeof(source);
|
||||
int nread = recvfrom(sock,cmdbuf,sizeof(cmdbuf)-1,0,(struct sockaddr*)&source,&sourceSize);
|
||||
cmdbuf[nread]='\0';
|
||||
LOG(INFO) << "received command \"" << cmdbuf << "\" from " << source.sun_path;
|
||||
std::ostringstream sout;
|
||||
int res = gParser.process(cmdbuf,sout);
|
||||
const std::string rspString= sout.str();
|
||||
const char* rsp = rspString.c_str();
|
||||
LOG(INFO) << "sending " << strlen(rsp) << "-char result to " << source.sun_path;
|
||||
if (sendto(sock,rsp,strlen(rsp)+1,0,(struct sockaddr*)&source,sourceSize)<0) {
|
||||
LOG(ERR) << "can't send CLI response to " << source.sun_path;
|
||||
}
|
||||
free(inbuf);
|
||||
#else // HAVE_LIBREADLINE ][
|
||||
cout << endl << gConfig.getStr("CLI.Prompt");
|
||||
cout.flush();
|
||||
char inbuf[1024];
|
||||
cin.getline(inbuf,1024,'\n');
|
||||
// The parser returns -1 on exit.
|
||||
if (gParser.process(inbuf,cout,cin)<0) break;
|
||||
#endif // !HAVE_LIBREADLINE ]
|
||||
// res<0 means to exit the application
|
||||
if (res<0) break;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBREADLINE // [
|
||||
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 // HAVE_LIBREADLINE ]
|
||||
|
||||
if (gTransceiverPid) kill(gTransceiverPid, SIGKILL);
|
||||
|
||||
|
||||
}
|
||||
} // try
|
||||
|
||||
catch (ConfigurationTableKeyNotFound e) {
|
||||
|
||||
LOG(ALERT) << "configuration key " << e.key() << " not defined";
|
||||
exit(2);
|
||||
LOG(ALERT) << "required configuration key " << e.key() << " not defined, aborting";
|
||||
}
|
||||
|
||||
if (gTransceiverPid) kill(gTransceiverPid, SIGKILL);
|
||||
close(sock);
|
||||
|
||||
}
|
||||
|
||||
// vim: ts=4 sw=4
|
||||
|
@@ -1,7 +1,7 @@
|
||||
PRAGMA foreign_keys=OFF;
|
||||
BEGIN TRANSACTION;
|
||||
CREATE TABLE CONFIG ( KEYSTRING TEXT UNIQUE NOT NULL, VALUESTRING TEXT, STATIC INTEGER DEFAULT 0, OPTIONAL INTEGER DEFAULT 0, COMMENTS TEXT DEFAULT '');
|
||||
INSERT INTO "CONFIG" VALUES('CLI.Prompt','OpenBTS> ',0,0,'Prompt for the OpenBTS command line interface.');
|
||||
INSERT INTO "CONFIG" VALUES('CLI.SocketPath','/var/run/command',0,0,'Path for Unix domain datagram socket used for the OpenBTS console interface.');
|
||||
INSERT INTO "CONFIG" VALUES('Control.Reporting.PhysStatusTable','/var/run/OpenBTSChannelTable.db',1,0,'File path for channel status reporting database. Static.');
|
||||
INSERT INTO "CONFIG" VALUES('Control.Reporting.TMSITable','/var/run/OpenBTSTMSITable.db',1,0,'File path for TMSITable database. Static.');
|
||||
INSERT INTO "CONFIG" VALUES('Control.Call.QueryRRLP.Early',NULL,0,1,'If not NULL, query every MS for its location via RRLP during the setup of a call.');
|
||||
|
170
apps/OpenBTSCLI.cpp
Normal file
170
apps/OpenBTSCLI.cpp
Normal file
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* Copyright 2011 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 Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
|
||||
#define HAVE_LIBREADLINE
|
||||
|
||||
|
||||
#ifdef HAVE_LIBREADLINE
|
||||
# include <readline/readline.h>
|
||||
# include <readline/history.h>
|
||||
#endif
|
||||
|
||||
|
||||
#define DEFAULT_CMD_PATH "/var/run/command"
|
||||
#define DEFAULT_RSP_PATH "./response"
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
const char* cmdPath = DEFAULT_CMD_PATH;
|
||||
if (argc!=1) {
|
||||
cmdPath = argv[1];
|
||||
}
|
||||
|
||||
char rspPath[200];
|
||||
sprintf(rspPath,"/tmp/OpenBTS.console.%d.%8x",getpid(),time(NULL));
|
||||
|
||||
|
||||
printf("command socket path is %s\n", cmdPath);
|
||||
|
||||
char prompt[strlen(cmdPath) + 20];
|
||||
sprintf(prompt,"OpenBTS> ");
|
||||
|
||||
// the socket
|
||||
int sock = socket(AF_UNIX,SOCK_DGRAM,0);
|
||||
if (sock<0) {
|
||||
perror("opening datagram socket");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// destination address
|
||||
struct sockaddr_un cmdSockName;
|
||||
cmdSockName.sun_family = AF_UNIX;
|
||||
strcpy(cmdSockName.sun_path,cmdPath);
|
||||
|
||||
// locally bound address
|
||||
struct sockaddr_un rspSockName;
|
||||
rspSockName.sun_family = AF_UNIX;
|
||||
char rmcmd[strlen(rspPath)+5];
|
||||
sprintf(rmcmd,"rm %s",rspPath);
|
||||
system(rmcmd);
|
||||
strcpy(rspSockName.sun_path,rspPath);
|
||||
if (bind(sock, (struct sockaddr *) &rspSockName, sizeof(struct sockaddr_un))) {
|
||||
perror("binding name to datagram socket");
|
||||
exit(1);
|
||||
}
|
||||
printf("response socket bound to %s\n",rspSockName.sun_path);
|
||||
|
||||
#ifdef HAVE_LIBREADLINE
|
||||
// start console
|
||||
using_history();
|
||||
|
||||
static const char * const history_file_name = "/.openbts_history";
|
||||
char *history_name = 0;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
printf("readline installed\n");
|
||||
#endif
|
||||
|
||||
|
||||
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");
|
||||
|
||||
|
||||
while (1) {
|
||||
|
||||
#ifdef HAVE_LIBREADLINE
|
||||
char *cmd = readline(prompt);
|
||||
if (!cmd) break;
|
||||
if (*cmd) add_history(cmd);
|
||||
#else // HAVE_LIBREADLINE
|
||||
printf("%s",prompt);
|
||||
fflush(stdout);
|
||||
char *inbuf = (char*)malloc(200);
|
||||
char *cmd = fgets(inbuf,199,stdin);
|
||||
if (!cmd) continue;
|
||||
// strip trailing CR
|
||||
cmd[strlen(cmd)-1] = '\0';
|
||||
#endif
|
||||
|
||||
// local quit?
|
||||
if (strcmp(cmd,"quit")==0) {
|
||||
printf("closing remote console\n");
|
||||
break;
|
||||
}
|
||||
if (sendto(sock,cmd,strlen(cmd)+1,0,(struct sockaddr*)&cmdSockName,sizeof(cmdSockName))<0) {
|
||||
perror("sending datagram");
|
||||
printf("Is the remote application running?\n");
|
||||
continue;
|
||||
}
|
||||
free(cmd);
|
||||
const int bufsz = 10000;
|
||||
char resbuf[bufsz];
|
||||
int nread = recv(sock,resbuf,bufsz-1,0);
|
||||
if (nread<0) {
|
||||
perror("receiving response");
|
||||
continue;
|
||||
}
|
||||
resbuf[nread] = '\0';
|
||||
printf("%s\n",resbuf);
|
||||
if (nread==(bufsz-1)) printf("(response truncated at %d characters)\n",nread);
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBREADLINE
|
||||
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);
|
||||
}
|
91
apps/OpenBTSDo.cpp
Normal file
91
apps/OpenBTSDo.cpp
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2011 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 Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
// KEEP THIS FILE CLEAN FOR GPL PUBLIC RELEASE.
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
|
||||
|
||||
#define DEFAULT_CMD_PATH "/var/run/command"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
const char* cmdPath = DEFAULT_CMD_PATH;
|
||||
if (argc!=1) {
|
||||
cmdPath = argv[1];
|
||||
}
|
||||
|
||||
char rspPath[200];
|
||||
sprintf(rspPath,"/tmp/OpenBTS.do.%d",getpid());
|
||||
|
||||
// the socket
|
||||
int sock = socket(AF_UNIX,SOCK_DGRAM,0);
|
||||
if (sock<0) {
|
||||
perror("opening datagram socket");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// destination address
|
||||
struct sockaddr_un cmdSockName;
|
||||
cmdSockName.sun_family = AF_UNIX;
|
||||
strcpy(cmdSockName.sun_path,cmdPath);
|
||||
|
||||
// locally bound address
|
||||
struct sockaddr_un rspSockName;
|
||||
rspSockName.sun_family = AF_UNIX;
|
||||
strcpy(rspSockName.sun_path,rspPath);
|
||||
if (bind(sock, (struct sockaddr *) &rspSockName, sizeof(struct sockaddr_un))) {
|
||||
perror("binding name to datagram socket");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
char *inbuf = (char*)malloc(200);
|
||||
char *cmd = fgets(inbuf,199,stdin);
|
||||
if (!cmd) exit(0);
|
||||
|
||||
if (sendto(sock,cmd,strlen(cmd)+1,0,(struct sockaddr*)&cmdSockName,sizeof(cmdSockName))<0) {
|
||||
perror("sending datagram");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
const int bufsz = 1500;
|
||||
char resbuf[bufsz];
|
||||
int nread = recv(sock,resbuf,bufsz-1,0);
|
||||
if (nread<0) {
|
||||
perror("receiving response");
|
||||
exit(1);
|
||||
}
|
||||
resbuf[nread] = '\0';
|
||||
printf("%s\n",resbuf);
|
||||
|
||||
close(sock);
|
||||
}
|
Reference in New Issue
Block a user