Browse Source

Coming together

master
David Ludwig 4 years ago
parent
commit
ee6cf9d194
13 changed files with 194 additions and 35 deletions
  1. +0
    -0
      .env
  2. +5
    -0
      .env.example
  3. +1
    -0
      .gitignore
  4. +13
    -5
      src/application.cpp
  5. +1
    -0
      src/application.h
  6. +8
    -0
      src/interface/ipcserver.cpp
  7. +1
    -0
      src/interface/ipcsocketinterface.cpp
  8. +92
    -6
      src/interface/torrentclientinterface.cpp
  9. +12
    -1
      src/interface/torrentclientinterface.h
  10. +35
    -0
      src/main.cpp
  11. +1
    -1
      src/torrent/alertmanager.cpp
  12. +24
    -21
      src/torrent/torrentclient.cpp
  13. +1
    -1
      src/torrent/torrentclient.h

+ 0
- 0
.env View File


+ 5
- 0
.env.example View File

@ -0,0 +1,5 @@
# The directory where data is stored
DATA_PATH = /var/autoplex/torrent
# The full path to the IPC socket file to save
IPC_SOCKET_FILE = /var/autoplex/ipc/torrent_client.sock

+ 1
- 0
.gitignore View File

@ -2,6 +2,7 @@
# ----------------------------------------------------------------------------
*~
.env
*.autosave
*.a
*.core


+ 13
- 5
src/application.cpp View File

@ -7,15 +7,23 @@ Application::Application(int argc, char *argv[])
bool Application::boot()
{
auto env = QProcessEnvironment::systemEnvironment();
if (!env.contains("DATA_PATH") || !env.contains("IPC_SOCKET_FILE")) {
qDebug() << "Missing DATA_PATH and IPC_SOCKET_FILE environment variables";
return false;
}
// Boot the torrent client
// m_torrentClient = new TorrentClient();
// if (!m_torrentClient->boot()) {
// return false;
// }
m_torrentClient = new TorrentClient(QDir("/var/autoplex/torrent"));
if (!m_torrentClient->boot()) {
qDebug() << "Torrent client failed to boot";
return false;
}
// Boot the IPC socket server interface
m_ipcServer = new IpcServer(m_torrentClient, "/var/autoplex/ipc/torrent_client.sock");
m_ipcServer = new IpcServer(m_torrentClient, env.value("IPC_SOCKET_FILE"));
if (!m_ipcServer->boot()) {
qDebug() << "IPC server failed to boot";
return false;
}
return true;


+ 1
- 0
src/application.h View File

@ -3,6 +3,7 @@
#include <QCoreApplication>
#include <QObject>
#include <QProcessEnvironment>
#include "./interface/ipcserver.h"
#include "./torrent/torrentclient.h"


+ 8
- 0
src/interface/ipcserver.cpp View File

@ -20,10 +20,18 @@ IpcServer::IpcServer(TorrentClient *client, QString socketPath) : QObject()
*/
bool IpcServer::boot()
{
// Create the path to the IPC socket file
if (!QDir().mkpath(QFileInfo(m_socketPath).absolutePath())) {
qDebug() << "Failed to create IPC path";
return false;
}
// Create the IPC socket server
m_server = new QLocalServer();
m_server->setSocketOptions(QLocalServer::UserAccessOption);
m_server->setMaxPendingConnections(1);
if (!m_server->listen(m_socketPath)) {
qDebug() << "Failed to create IPC socket file";
return false;
}


+ 1
- 0
src/interface/ipcsocketinterface.cpp View File

@ -49,6 +49,7 @@ void IpcSocketInterface::readBytes()
*/
void IpcSocketInterface::writeBytes(const QByteArray data)
{
qDebug() << data;
m_socket->write(data);
m_socket->write("\f");
m_socket->flush();


+ 92
- 6
src/interface/torrentclientinterface.cpp View File

@ -7,9 +7,9 @@
*/
TorrentClientInterface::TorrentClientInterface(TorrentClient *client)
: QObject(),
m_torrentClient(client),
m_metaObject(staticMetaObject),
m_metaEnum(m_metaObject.enumerator(m_metaObject.indexOfEnumerator("RequestType")))
m_metaEnum(m_metaObject.enumerator(m_metaObject.indexOfEnumerator("RequestType"))),
m_torrentClient(client)
{
}
@ -31,14 +31,15 @@ void TorrentClientInterface::exec(QByteArray data)
qDebug() << "Invalid request received";
return;
}
if (!requestPacket["type"].isString() || !requestPacket["data"].isObject()) {
if (!requestPacket["type"].isString()) {
qDebug() << "Malformed request received";
return;
}
// Perform the request
QString requestType = requestPacket["type"].toString();
QJsonValueRef body = requestPacket["body"];
QJsonValueRef body = requestPacket["data"];
QJsonValue *responseData = request(requestType, body);
if (responseData == nullptr) {
return;
@ -52,7 +53,7 @@ void TorrentClientInterface::exec(QByteArray data)
delete responseData;
}
// -------------------------------------------------------------------------------------------------
// Request Handling --------------------------------------------------------------------------------
/**
* Match and perform the provided request
@ -75,14 +76,99 @@ QJsonValue* TorrentClientInterface::request(const QString type, const QJsonValue
*/
QJsonValue* TorrentClientInterface::request(const RequestType type, const QJsonValueRef &body)
{
qDebug() << "Requesting:" << type << body;
switch(type) {
case request_add : return requestAdd(body);
case request_list: return requestList(body);
default:
return nullptr;
}
}
// -------------------------------------------------------------------------------------------------
// Response Generation -----------------------------------------------------------------------------
/**
* Create an error response
*
* @param message
* @return
*/
QJsonValue* TorrentClientInterface::createErrorResponse(const int code, const char* message)
{
return createErrorResponse(code, QString::fromStdString(message));
}
/**
* Create an error response
*
* @param message
* @return
*/
QJsonValue* TorrentClientInterface::createErrorResponse(const int code, std::string message)
{
return createErrorResponse(code, QString::fromStdString(message));
}
/**
* Create an error response
*
* @param message
* @return
*/
QJsonValue* TorrentClientInterface::createErrorResponse(const int code, const QString message)
{
QJsonObject response;
response["status"] = "error";
response["message"] = message;
response["error_code"] = code;
return new QJsonValue(response);
}
/**
* Create a successful response
*
* @param data
* @return
*/
QJsonValue* TorrentClientInterface::createResponse(QJsonValue data)
{
QJsonObject response;
response["status"] = "success";
response["data"] = data;
return new QJsonValue(response);
}
// Request Implementations -------------------------------------------------------------------------
/**
* Add a torrent to the client
*
* @param body
* @return
*/
QJsonValue* TorrentClientInterface::requestAdd(const QJsonValueRef &body)
{
qDebug() << "Adding torrent";
if (!body.isString()) {
return createErrorResponse(-1, "Value is not a string");
}
// Add the torrent
QString magnetLink = body.toString();
lt::sha1_hash infoHash;
lt::error_code error;
m_torrentClient->addTorrent(magnetLink, infoHash, error);
// Check for an error
if (error.value() != lt::errors::no_error) {
return createErrorResponse(error.value(), error.message());
}
QJsonObject response;
response["info_hash"] = QString::fromStdString(infoHash.to_string());
return createResponse(QJsonValue(response));
}
/**
* Request the list of torrents from the client


+ 12
- 1
src/interface/torrentclientinterface.h View File

@ -8,6 +8,8 @@
#include <QJsonValue>
#include <QMetaEnum>
#include <QObject>
#include <string>
#include <libtorrent/error_code.hpp>
#include "../torrent/torrentclient.h"
class TorrentClientInterface : public QObject
@ -21,6 +23,7 @@ class TorrentClientInterface : public QObject
public:
enum RequestType
{
request_add,
request_list
};
@ -29,14 +32,22 @@ public:
protected:
virtual void writeBytes(const QByteArray data) = 0;
// Request handling
QJsonValue* request(const QString type, const QJsonValueRef &body);
QJsonValue* request(const RequestType type, const QJsonValueRef &body);
// Response handling
QJsonValue* createErrorResponse(const int code, const char*);
QJsonValue* createErrorResponse(const int code, std::string);
QJsonValue* createErrorResponse(const int code, QString message);
QJsonValue* createResponse(QJsonValue data);
// Interface methods
QJsonValue* requestAdd(const QJsonValueRef &body);
QJsonValue* requestList(const QJsonValueRef &body);
private:
const TorrentClient *m_torrentClient;
TorrentClient *m_torrentClient;
public slots:
void exec(QByteArray data);


+ 35
- 0
src/main.cpp View File

@ -1,8 +1,43 @@
#include <signal.h>
#include "application.h"
void ignoreUnixSignals(std::initializer_list<int> ignoreSignals) {
// all these signals will be ignored.
for (int sig : ignoreSignals) {
signal(sig, SIG_IGN);
}
}
void catchUnixSignals(std::initializer_list<int> quitSignals) {
auto handler = [](int) -> void {
QCoreApplication::quit();
};
sigset_t blocking_mask;
sigemptyset(&blocking_mask);
for (auto sig : quitSignals) {
sigaddset(&blocking_mask, sig);
}
struct sigaction sa;
sa.sa_handler = handler;
sa.sa_mask = blocking_mask;
sa.sa_flags = 0;
for (auto sig : quitSignals) {
sigaction(sig, &sa, nullptr);
}
}
int main(int argc, char *argv[])
{
Application a(argc, argv);
// Catch signals to allow proper shutdown of the application
catchUnixSignals({ SIGQUIT, SIGINT, SIGTERM, SIGHUP });
// Allow cout to automatically flush
std::cout << std::unitbuf;
return a.exec();
}

+ 1
- 1
src/torrent/alertmanager.cpp View File

@ -10,7 +10,7 @@
AlertManager::AlertManager(QObject *parent) : QObject(parent)
{
connect(this, SIGNAL(alertsReady(lt::session_handle*)), this, SLOT(dispatchAlertsForSession(lt::session_handle*)));
connect(this, &AlertManager::alertsReady, this, &AlertManager::dispatchAlertsForSession);
}
void AlertManager::addSession(lt::session_handle *session)


+ 24
- 21
src/torrent/torrentclient.cpp View File

@ -11,8 +11,6 @@ qint64 totalTime = 0;
TorrentClient::TorrentClient(QDir dataDir)
: QObject(), m_dataDir(dataDir), m_sessionDataFile(dataDir.absoluteFilePath(".session").toStdString())
{
dataDir.mkpath(".");
m_alertManager = new AlertManager();
m_numOutstandingResumeData = 0;
@ -30,8 +28,13 @@ TorrentClient::TorrentClient(QDir dataDir)
/**
* Boot the torrent client
*/
int TorrentClient::boot()
bool TorrentClient::boot()
{
if (!m_dataDir.mkpath(".")) {
qDebug() << "Failed to create data directory";
return false;
}
qDebug() << "Booting session...";
createSession();
loadTorrentsFromDisk();
@ -40,7 +43,20 @@ int TorrentClient::boot()
lt::sha1_hash hash;
lt::error_code error;
return 0;
return true;
}
void TorrentClient::shutdown()
{
qDebug() << "Shutting down...";
QEventLoop loop;
connect(this, SIGNAL(allTorrentsSaved()), &loop, SLOT(quit()));
m_session->pause();
saveSession();
saveTorrents();
loop.exec();
delete m_session;
qDebug() << "Shutdown complete!";
}
/**
@ -99,7 +115,7 @@ void TorrentClient::saveTorrent(const lt::torrent_handle &torrent, lt::resume_da
{
torrent.save_resume_data(flags);
incNumOutstandingResumeData(1);
qDebug() << "Saving a torrent file...";
std::cout << "Saving torrent: " << torrent.name() << std::endl;
}
void TorrentClient::saveTorrents()
@ -115,20 +131,7 @@ void TorrentClient::saveSession()
of.unsetf(std::ios_base::skipws);
auto const buffer = write_session_params_buf(m_session->session_state(), lt::save_state_flags_t::all());
of.write(buffer.data(), int(buffer.size()));
qDebug() << "Saved";
}
void TorrentClient::shutdown()
{
qDebug() << "Shutting down...";
QEventLoop loop;
connect(this, SIGNAL(allTorrentsSaved()), &loop, SLOT(quit()));
m_session->pause();
saveSession();
saveTorrents();
loop.exec();
delete m_session;
qDebug() << "Shutdown complete!";
std::cout << "Session saved";
}
// Event Handling ---------------------------------------------------------------------------------
@ -136,7 +139,7 @@ void TorrentClient::shutdown()
void TorrentClient::addTorrentAlert(const lt::add_torrent_alert *alert)
{
if (alert->error) {
qDebug() << "Failed to add torrent. " << QString::fromStdString(alert->error.message());
std::cout << "Failed to add torrent. " << alert->error.message() << std::endl;
return;
}
saveTorrent(alert->handle, lt::torrent_handle::save_info_dict | lt::torrent_handle::only_if_modified);
@ -177,7 +180,7 @@ void TorrentClient::saveResumeDataFailedAlert(const lt::save_resume_data_failed_
void TorrentClient::torrentFinishedAlert(const lt::torrent_finished_alert *alert)
{
qDebug() << "The torrent finished!" << (QDateTime::currentMSecsSinceEpoch() - totalTime);
std::cout << "Torrent finished: '" << alert->torrent_name() << "'" << std::endl;
}
void TorrentClient::incNumOutstandingResumeData(int value)


+ 1
- 1
src/torrent/torrentclient.h View File

@ -36,7 +36,7 @@ public:
AlertManager *alertManager();
public slots:
int boot();
bool boot();
void shutdown();
bool addTorrent(QString magnetLink, lt::sha1_hash &resultHash, lt::error_code &error);


Loading…
Cancel
Save