Allow only one instance for useLocalServer option activated. If exists,

pass to it arguments.

For now, only allow to pass RsCollection file and retroshare://
protocol.
This commit is contained in:
Phenom 2016-03-01 13:08:33 +01:00
parent 9871b37ef2
commit 10bb542e83
19 changed files with 547 additions and 426 deletions

View file

@ -20,51 +20,67 @@
* Boston, MA 02110-1301, USA.
****************************************************************/
#include <QDir>
#include <QTimer>
#include <QTextStream>
#include <QShortcut>
#include <QStyleFactory>
#include <QStyle>
#include <QString>
#include <QLocale>
#include <QRegExp>
#include <QBuffer>
#include <QDateTime>
#include <gui/common/vmessagebox.h>
#include <gui/common/html.h>
#include <util/stringutil.h>
#include <stdlib.h>
#include <QDir>
#include <QFileOpenEvent>
#include <QLocale>
#include <QLocalSocket>
#include <QRegExp>
#include <QSharedMemory>
#include <QShortcut>
#include <QString>
#include <QStyle>
#include <QStyleFactory>
#include <QTextStream>
#include <QTimer>
#ifdef __APPLE__
#include <QUrl>
#endif
#include <iostream>
#include <stdlib.h>
#include <gui/common/html.h>
#include <gui/common/vmessagebox.h>
#include <gui/gxs/GxsIdDetails.h>
#include <gui/settings/rsharesettings.h>
#include <lang/languagesupport.h>
#include <util/stringutil.h>
#include <retroshare/rsinit.h>
#include <retroshare/rsversion.h>
#include <retroshare/rsplugin.h>
#include <lang/languagesupport.h>
#include "gui/settings/rsharesettings.h"
#include "gui/gxs/GxsIdDetails.h"
#include "rshare.h"
/* Available command-line arguments. */
#define ARG_LANGUAGE "lang" /**< Argument specifying language. */
#define ARG_GUISTYLE "style" /**< Argument specfying GUI style. */
#define ARG_LANGUAGE "lang" /**< Argument specifying language. */
#define ARG_GUISTYLE "style" /**< Argument specfying GUI style. */
#define ARG_GUISTYLESHEET "stylesheet" /**< Argument specfying GUI style. */
#define ARG_RESET "reset" /**< Reset Rshare's saved settings. */
#define ARG_DATADIR "datadir" /**< Directory to use for data files. */
#define ARG_LOGFILE "logfile" /**< Location of our logfile. */
#define ARG_LOGLEVEL "loglevel" /**< Log verbosity. */
#define ARG_RESET "reset" /**< Reset Rshare's saved settings. */
#define ARG_DATADIR "datadir" /**< Directory to use for data files. */
#define ARG_LOGFILE "logfile" /**< Location of our logfile. */
#define ARG_LOGLEVEL "loglevel" /**< Log verbosity. */
#define ARG_RSLINK_S "r" /**< Open RsLink with protocol retroshare:// */
#define ARG_RSLINK_L "link" /**< Open RsLink with protocol retroshare:// */
#define ARG_RSFILE_S "f" /**< Open RsFile with or without arg */
#define ARG_RSFILE_L "rsfile" /**< Open RsFile with or without arg */
//Other defined for server in /libretroshare/src/rsserver/rsinit.cc:351
/* Static member variables */
QMap<QString, QString> Rshare::_args; /**< List of command-line arguments. */
QString Rshare::_style; /**< The current GUI style. */
QString Rshare::_language; /**< The current language. */
QString Rshare::_stylesheet; /**< The current GUI stylesheet. */
QString Rshare::_language; /**< The current language. */
QString Rshare::_dateformat; /**< The format of dates in feed items etc. */
Log Rshare::_log;
bool Rshare::useConfigDir;
QString Rshare::configDir;
Log Rshare::_log; /**< Logs debugging messages to file or stdout. */
QStringList Rshare::_links; /**< List of links passed by arguments. */
QStringList Rshare::_files; /**< List of files passed by arguments. */
QDateTime Rshare::mStartupTime;
bool Rshare::useConfigDir;
QString Rshare::configDir;
QLocalServer* Rshare::localServer;
/** Catches debugging messages from Qt and sends them to RetroShare's logs. If Qt
* emits a QtFatalMsg, we will write the message to the log and then abort().
@ -88,6 +104,10 @@ void qt_msg_handler(QtMsgType type, const char *msg)
case QtFatalMsg:
rError(QString("QtFatalMsg: %1").arg(msg));
break;
#if QT_VERSION >= QT_VERSION_CHECK (5, 5, 0)
case QtInfoMsg:
break;
#endif
}
if (type == QtFatalMsg) {
rError("Fatal Qt error. Aborting.");
@ -102,6 +122,61 @@ Rshare::Rshare(QStringList args, int &argc, char **argv, const QString &dir)
: QApplication(argc, argv)
{
mStartupTime = QDateTime::currentDateTime();
localServer = NULL;
//Initialize connection to LocalServer to know if other process runs.
{
QString serverName = QString(TARGET);
if (args.isEmpty()) args.append("Empty");
// load into shared memory
QBuffer buffer;
buffer.open(QBuffer::ReadWrite);
QDataStream out(&buffer);
out << args;
int size = buffer.size();
QSharedMemory newArgs;
newArgs.setKey(serverName + "_newArgs");
if (newArgs.isAttached()) newArgs.detach();
if (!newArgs.create(size)) {
std::cerr << "(EE) Rshare::Rshare Unable to create shared memory segment of size:"
<< size << " error:" << newArgs.errorString().toStdString() << "." << std::endl;
#ifdef Q_OS_UNIX
std::cerr << "Look with `ipcs -m` for nattch==0 segment. And remove it with `ipcrm -m 'shmid'`." << std::endl;
//No need for windows, as it removes shared segment directly even when crash.
#endif
newArgs.detach();
::exit(EXIT_FAILURE);
}
newArgs.lock();
char *to = (char*)newArgs.data();
const char *from = buffer.data().data();
memcpy(to, from, qMin(newArgs.size(), size));
newArgs.unlock();
// Connect to the Local Server of the main process to notify it
// that a new process had been started
QLocalSocket localSocket;
localSocket.connectToServer(QString(TARGET));
std::cerr << "Rshare::Rshare waitForConnected to other instance." << std::endl;
if( localSocket.waitForConnected(100) )
{
std::cerr << "Rshare::Rshare Connection etablished. Waiting for disconnection." << std::endl;
localSocket.waitForDisconnected(1000);
newArgs.detach();
::exit(EXIT_SUCCESS); // Terminate the program using STDLib's exit function
} else {
newArgs.detach();
// No main process exists
// So we start a Local Server to listen for connections from new process
localServer= new QLocalServer();
QObject::connect(localServer, SIGNAL(newConnection()), this, SLOT(slotConnectionEstablished()));
updateLocalServer();
}
}
#if QT_VERSION >= QT_VERSION_CHECK (5, 0, 0)
qInstallMessageHandler(qt_msg_handler);
@ -172,6 +247,48 @@ Rshare::~Rshare()
{
/* Cleanup GxsIdDetails */
GxsIdDetails::cleanup();
if (localServer)
{
localServer->close();
delete localServer;
}
}
/**
* @brief Executed when new instance connect command is sent to LocalServer
*/
void Rshare::slotConnectionEstablished()
{
QLocalSocket *socket = localServer->nextPendingConnection();
socket->close();
delete socket;
QSharedMemory newArgs;
newArgs.setKey(QString(TARGET) + "_newArgs");
if (!newArgs.attach())
{
std::cerr << "(EE) Rshare::slotConnectionEstablished() Unable to attach to shared memory segment."
<< newArgs.errorString().toStdString() << std::endl;
return;
}
QBuffer buffer;
QDataStream in(&buffer);
QStringList args;
newArgs.lock();
buffer.setData((char*)newArgs.constData(), newArgs.size());
buffer.open(QBuffer::ReadOnly);
in >> args;
newArgs.unlock();
newArgs.detach();
emit newArgsReceived(args);
while (!args.empty())
{
std::cerr << "Rshare::slotConnectionEstablished args:" << QString(args.takeFirst()).toStdString() << std::endl;
}
}
QString Rshare::retroshareVersion(bool withRevision)
@ -271,39 +388,62 @@ Rshare::showUsageMessageBox()
bool
Rshare::argNeedsValue(QString argName)
{
return (argName == ARG_GUISTYLE ||
argName == ARG_GUISTYLESHEET ||
argName == ARG_LANGUAGE ||
argName == ARG_DATADIR ||
argName == ARG_LOGFILE ||
argName == ARG_LOGLEVEL);
return (argName == ARG_GUISTYLE ||
argName == ARG_GUISTYLESHEET ||
argName == ARG_LANGUAGE ||
argName == ARG_DATADIR ||
argName == ARG_LOGFILE ||
argName == ARG_LOGLEVEL ||
argName == ARG_RSLINK_S ||
argName == ARG_RSLINK_L ||
argName == ARG_RSFILE_S ||
argName == ARG_RSFILE_L );
}
/** Parses the list of command-line arguments for their argument names and
* values. */
void
Rshare::parseArguments(QStringList args)
Rshare::parseArguments(QStringList args, bool firstRun)
{
QString arg, value;
QString arg, value;
/* Loop through all command-line args/values and put them in a map */
for (int i = 0; i < args.size(); ++i) {
/* Get the argument name and set a blank value */
arg = args.at(i).toLower();
value = "";
/* Loop through all command-line args/values and put them in a map */
for (int i = 0; i < args.size(); ++i) {
/* Get the argument name and set a blank value */
arg = args.at(i);//.toLower(); Need Upper case for file name.
if (arg.toLower() == "empty") continue;
value = "";
/* Check if it starts with a - or -- */
if (arg.startsWith("-")) {
arg = arg.mid((arg.startsWith("--") ? 2 : 1));
}
/* Check if it takes a value and there is one on the command-line */
if (i < args.size()-1 && argNeedsValue(arg)) {
value = args.at(++i);
}
/* Place this arg/value in the map */
_args.insert(arg, value);
}
/* Check if it starts with a - or -- */
if (arg.startsWith("-")) {
arg = arg.mid((arg.startsWith("--") ? 2 : 1));
/* Check if it takes a value and there is one on the command-line */
if (i < args.size()-1 && argNeedsValue(arg.toLower())) {
value = args.at(++i);
}
} else {
/* Check if links or files without arg */
if (arg.toLower().startsWith("retroshare://")) {
value = arg;
arg = ARG_RSLINK_L;
} else {
if (QFile(arg).exists()) {
value = arg;
arg = ARG_RSFILE_L;
}
}
}
/* Don't send theses argument to _args map to allow multiple. */
if (arg == ARG_RSLINK_S || arg == ARG_RSLINK_L) {
_links.append(value);
} else if (arg == ARG_RSFILE_S || arg == ARG_RSFILE_L) {
_files.append(value);
} else if (firstRun) {
/* Place this arg/value in the map only first time*/
_args.insert(arg, value);
}
}
}
/** Verifies that all specified arguments were valid. */
@ -660,6 +800,29 @@ Rshare::createShortcut(const QKeySequence &key, QWidget *sender,
connect(s, SIGNAL(activated()), receiver, slot);
}
#ifdef __APPLE__
bool Rshare::event(QEvent *event)
{
switch (event->type()) {
case QEvent::FileOpen:{
QFileOpenEvent* fileOE = static_cast<QFileOpenEvent *>(event);
QStringList args;
if (! fileOE->file().isEmpty()) {
_files.append(fileOE->file());
emit newArgsReceived(QStringList());
return true;
} else if (! fileOE->url().isEmpty()) {
_links.append(fileOE->url().toString());
emit newArgsReceived(QStringList());
return true;
}
}
default:
return QApplication::event(event);
}
}
#endif
void Rshare::blinkTimer()
{
mBlink = !mBlink;
@ -706,3 +869,17 @@ bool Rshare::loadCertificate(const RsPeerId &accountId, bool autoLogin)
return true;
}
bool Rshare::updateLocalServer()
{
if (localServer) {
QString serverName = QString(TARGET);
if (Settings->getUseLocalServer()) {
localServer->removeServer(serverName);
localServer->listen(serverName);
return true;
}
localServer->close();
}
return false;
}