From 7526965b26813296f8f622ada94ed02c54ee78b0 Mon Sep 17 00:00:00 2001 From: xarkes Date: Mon, 29 Jan 2018 15:13:16 +0100 Subject: [PATCH] Added listening r2 server for Jupyter + r2pipe --- src/MainWindow.cpp | 11 ++-- src/cutter.pro | 6 ++- src/utils/CommandServer.cpp | 89 +++++++++++++++++++++++++++++++++ src/utils/CommandServer.h | 26 ++++++++++ src/utils/JupyterConnection.cpp | 20 +++++++- src/utils/JupyterConnection.h | 3 +- 6 files changed, 143 insertions(+), 12 deletions(-) create mode 100644 src/utils/CommandServer.cpp create mode 100644 src/utils/CommandServer.h diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index bb1cfae4..13bd79f3 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -83,7 +83,7 @@ static void registerCustomFonts() ret = QFontDatabase::addApplicationFont(":/fonts/Inconsolata-Regular.ttf"); assert(-1 != ret && "unable to register Inconsolata-Regular.ttf"); - // do not issue a warning in release + // Do not issue a warning in release Q_UNUSED(ret) } @@ -99,6 +99,7 @@ MainWindow::MainWindow(QWidget *parent) : MainWindow::~MainWindow() { + delete configuration; } void MainWindow::initUI() @@ -389,22 +390,18 @@ void MainWindow::closeEvent(QCloseEvent *event) QMessageBox::StandardButton ret = QMessageBox::question(this, APPNAME, tr("Do you really want to exit?\nSave your project before closing!"), (QMessageBox::StandardButtons)(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel)); - //qDebug() << ret; if (ret == QMessageBox::Save) { if (saveProject(true)) { saveSettings(); - QMainWindow::closeEvent(event); - } - else - { - event->ignore(); } + QMainWindow::closeEvent(event); } else if (ret == QMessageBox::Discard) { saveSettings(); + QMainWindow::closeEvent(event); } else { diff --git a/src/cutter.pro b/src/cutter.pro index a6e49cac..19713723 100644 --- a/src/cutter.pro +++ b/src/cutter.pro @@ -93,7 +93,8 @@ SOURCES += \ widgets/VTablesWidget.cpp \ CutterApplication.cpp \ utils/JupyterConnection.cpp \ - widgets/JupyterWidget.cpp + widgets/JupyterWidget.cpp \ + utils/CommandServer.cpp HEADERS += \ cutter.h \ @@ -156,7 +157,8 @@ HEADERS += \ CutterApplication.h \ widgets/VTablesWidget.h \ utils/JupyterConnection.h \ - widgets/JupyterWidget.h + widgets/JupyterWidget.h \ + utils/CommandServer.h FORMS += \ dialogs/AboutDialog.ui \ diff --git a/src/utils/CommandServer.cpp b/src/utils/CommandServer.cpp new file mode 100644 index 00000000..51308e93 --- /dev/null +++ b/src/utils/CommandServer.cpp @@ -0,0 +1,89 @@ +#include "CommandServer.h" +#include "cutter.h" + +CommandServer::CommandServer(QObject *parent) : QObject(parent) +{ + +} + +CommandServer::~CommandServer() +{ + +} + +/** + * @brief Open a local TCP server on port 1234 to read and execute commands. + * This is useful for Jupyter Notebook to access radare2 context using r2pipe + * Example usage: + * import r2pipe; r2 = r2pipe.open('tcp://localhost:1234'); print(r2.cmd('i')) + */ +bool CommandServer::startCommandServer() +{ + RCore* core = Core()->core(); + const char* port = "1234"; + + unsigned char buf[4097]; + RSocket *ch = NULL; + RSocket *s; + int i, ret; + char *str; + + s = r_socket_new (0); + r_socket_listen (s, port, NULL); + if (false) { + eprintf ("Error listening on port %s\n", port); + r_socket_free (s); + return false; + } + + eprintf ("Listening for commands on port %s\n", port); + while (isRunning) { + ch = r_socket_accept (s); + buf[0] = 0; + ret = r_socket_read (ch, buf, sizeof (buf) - 1); + if (ret > 0) { + buf[ret] = 0; + for (i = 0; buf[i]; i++) { + if (buf[i] == '\n') { + buf[i] = buf[i + 1]? ';': '\0'; + } + } + if ((!r_config_get_i (core->config, "scr.prompt") && + !strcmp ((char *)buf, "q!")) || + !strcmp ((char *)buf, ".--")) { + r_socket_close (ch); + break; + } + str = r_core_cmd_str (core, (const char *)buf); + if (str && *str) { + r_socket_write (ch, str, strlen (str)); + } else { + const char nl[] = "\n"; + r_socket_write (ch, (void*) nl, 1); + } + free (str); + } + r_socket_close (ch); + r_socket_free (ch); + ch = NULL; + } + r_socket_free (s); + r_socket_free (ch); + eprintf ("TCP Server (on %s) exited.\n", port); + + return true; +} + +void CommandServer::process() +{ + startCommandServer(); + emit finished(); +} + +/** + * Stops the server. + */ +void CommandServer::stop() +{ + isRunning = false; +} diff --git a/src/utils/CommandServer.h b/src/utils/CommandServer.h new file mode 100644 index 00000000..cf8d42c2 --- /dev/null +++ b/src/utils/CommandServer.h @@ -0,0 +1,26 @@ +#ifndef COMMANDSERVER_H +#define COMMANDSERVER_H + +#include + +class CommandServer : public QObject +{ + Q_OBJECT +public: + explicit CommandServer(QObject *parent = nullptr); + ~CommandServer(); + void stop(); + +signals: + void finished(); + void error(QString err); + +public slots: + void process(); + +private: + bool isRunning = true; + bool startCommandServer(); +}; + +#endif // COMMANDSERVER_H diff --git a/src/utils/JupyterConnection.cpp b/src/utils/JupyterConnection.cpp index a98ef8fd..289c7354 100644 --- a/src/utils/JupyterConnection.cpp +++ b/src/utils/JupyterConnection.cpp @@ -2,6 +2,8 @@ #include #include #include +#include +#include #include "JupyterConnection.h" @@ -12,6 +14,9 @@ JupyterConnection::JupyterConnection(QObject *parent) : QObject(parent) JupyterConnection::~JupyterConnection() { + cmdServer->stop(); + process->terminate(); + urlProcess->terminate(); } const char *urlPy = "from notebook import notebookapp\n" @@ -28,23 +33,34 @@ void JupyterConnection::start() process = new QProcess(this); connect(process, &QProcess::readyReadStandardError, this, &JupyterConnection::readStandardError); connect(process, &QProcess::readyReadStandardOutput, this, &JupyterConnection::readStandardOutput); + connect(process, &QProcess::errorOccurred, this, [](QProcess::ProcessError error){ qWarning() << "Jupyter error occurred:" << error; }); process->start("jupyter", {"notebook", "--no-browser", "-y"}); urlProcess = new QProcess(this); connect(urlProcess, &QProcess::readyReadStandardOutput, this, &JupyterConnection::readUrlStandardOutput); urlProcess->start("python3", {"-c", urlPy}); + + QThread *cmdServerThread = new QThread(this); + cmdServer = new CommandServer(); + cmdServer->moveToThread(cmdServerThread); + connect(cmdServer, &CommandServer::error, this, [](QString err){ qWarning() << "CmdServer error:" << err; }); + connect(cmdServerThread, SIGNAL (started()), cmdServer, SLOT (process())); + connect(cmdServer, SIGNAL (finished()), cmdServerThread, SLOT (quit())); + connect(cmdServer, SIGNAL (finished()), cmdServer, SLOT (deleteLater())); + connect(cmdServerThread, SIGNAL (finished()), cmdServerThread, SLOT (deleteLater())); + cmdServerThread->start(); } void JupyterConnection::readStandardError() { auto data = process->readAllStandardError(); - printf("jupyter stderr: %s\n", data.constData()); + printf("Jupyter stderr: %s", data.constData()); } void JupyterConnection::readStandardOutput() { auto data = process->readAllStandardOutput(); - printf("jupyter stdout: %s\n", data.constData()); + printf("Jupyter stdout: %s", data.constData()); } void JupyterConnection::readUrlStandardOutput() diff --git a/src/utils/JupyterConnection.h b/src/utils/JupyterConnection.h index a123f026..c8649e9d 100644 --- a/src/utils/JupyterConnection.h +++ b/src/utils/JupyterConnection.h @@ -1,8 +1,8 @@ - #ifndef JUPYTERCONNECTION_H #define JUPYTERCONNECTION_H #include +#include "CommandServer.h" class JupyterConnection : public QObject { @@ -20,6 +20,7 @@ signals: private: QProcess *process; QProcess *urlProcess; + CommandServer *cmdServer; private slots: void readStandardError();