#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../include/climanager.h" #include "../include/jsonhandler.h" using namespace std; thread readPipeLoopThread; thread notificationsLoopThread; thread statusLoopThread; namespace CliManager { QMLHandler *qmlHandler; bool programActive = false; bool loggedin = false; int inpipefd[2]; int outpipefd[2]; char buf[1025]; pid_t childpid; } // namespace CliManager void CliManager::writeToCli(QString command) { command += "\n"; write(outpipefd[1], command.toUtf8().constData(), strlen(command.toUtf8().constData())); } void CliManager::onExit() { // stop threads if (programActive) { writeToCli("exit"); CliManager::setProgramActive(false); readPipeLoopThread.join(); notificationsLoopThread.join(); statusLoopThread.join(); } } void CliManager::setQmlHandler(QMLHandler *q) { qmlHandler = q; } void CliManager::init(bool useSSL) { qInfo() << QString::fromStdString(string("InitCLI with path ") + Config::getValue("CLI-Path")); emit qmlHandler->log(QString::fromStdString(string("InitCLI with path ") + Config::getValue("CLI-Path"))); if (!Config::getValue("CLI-Path").size()) { qInfo() << "Empty CLI-Path!"; return; } pipe(inpipefd); pipe(outpipefd); childpid = fork(); if (childpid == 0) { // Child dup2(outpipefd[0], STDIN_FILENO); dup2(inpipefd[1], STDOUT_FILENO); // dup2(inpipefd[1], STDERR_FILENO); // ask kernel to deliver SIGTERM in case the parent dies prctl(PR_SET_PDEATHSIG, SIGTERM); if (useSSL) { execl(Config::getValue("CLI-Path").c_str(), "ccats-cli", "--machine", "--usessl", Config::getValue("SSL-Path").c_str(), (char *)NULL); } else { execl(Config::getValue("CLI-Path").c_str(), "ccats-cli", "--machine", (char *)NULL); } exit(1); } close(outpipefd[0]); close(inpipefd[1]); setProgramActive(true); if (Config::getValue("Keyfile-Path").size()) { QString cmd = QString("keyfile ") + Config::getValue("Keyfile-Path").c_str(); writeToCli(cmd); } // start threads readPipeLoopThread = thread(&CliManager::readPipeLoop); notificationsLoopThread = thread(&CliManager::notificationsLoop); statusLoopThread = thread(&CliManager::statusLoop); } std::vector tokenizeByNewlines(std::string in) { vector res; size_t index; for (index = in.find("\n"); index != std::string::npos; index = in.find("\n")) { if (index != 0) res.push_back(in.substr(0, index)); in = in.substr(index + 1); } if (in.length() > 0) res.push_back(in); return res; } void CliManager::readPipeLoop() { unsigned int readOffset = 0; unsigned int pollCount = 0; struct pollfd inPipeStatus; inPipeStatus.fd = inpipefd[0]; inPipeStatus.events = POLLIN; vector inputs; string pipeInput; while (programActive) { inputs = vector(); poll(&inPipeStatus, 1, 100); if (inPipeStatus.revents & POLLIN) { readOffset += read(inpipefd[0], buf + readOffset, 1); pollCount = 0; } else { pollCount++; } if (pollCount > 4 && (readOffset || pipeInput.size())) { pipeInput.append(buf); inputs = tokenizeByNewlines(pipeInput); for (string s : inputs) { JsonHandler::parseJSON(s); } pipeInput = string(); memset(buf, 0, 1025); pollCount = 0; readOffset = 0; if (waitpid(childpid, NULL, WNOHANG)) { qInfo() << "CLI has exited/did not launch"; emit qmlHandler->log("CLI has exited/did not launch"); emit qmlHandler->footerSetError("CLI crashed. Please restart the application."); setProgramActive(false); // nonzero means error or childid has changed state // for us that means child has exited -> CLI is dead break; } } if (readOffset >= 1024) { pipeInput.append(buf); inputs = tokenizeByNewlines(pipeInput); for (unsigned i = 0; i < inputs.size() - 1; i++) { // process all lines except the last, potentially incomplete one JsonHandler::parseJSON(inputs[i]); } if (pipeInput.back() == '\n') { // process last line if it was complete JsonHandler::parseJSON(inputs.back()); pipeInput = string(); } else { pipeInput = inputs[inputs.size() - 1]; // set last, potentially incomplete line, as current buffer } readOffset = 0; pollCount = 0; memset(buf, 0, 1025); } } } void CliManager::notificationsLoop() { while (programActive) { std::this_thread::sleep_for(std::chrono::seconds(3)); if (loggedin) { writeToCli("notifications"); writeToCli("extendedlist"); } } } void CliManager::statusLoop() { while (programActive) { std::this_thread::sleep_for(std::chrono::seconds(1)); if (loggedin) { writeToCli("status"); writeToCli("extendedstatus"); } } } void CliManager::setProgramActive(bool active) { programActive = active; }