From da6ad50759084f86e75a1699f680350ca5a8dc67 Mon Sep 17 00:00:00 2001 From: codestation Date: Sat, 14 Mar 2015 23:55:56 -0430 Subject: [PATCH] Removed dbus support. Added unix signals support. --- README.md | 26 ++++++---------- qcma.6 | 2 +- qcma.pro | 2 +- qcma_cli.6 | 21 +++++++------ qcma_cli.pro | 12 -------- qcma_gui.pro | 12 -------- src/cli/headlessmanager.cpp | 59 ++++++++++++++++++++++++++++++++----- src/cli/headlessmanager.h | 21 ++++++++++--- src/cli/main_cli.cpp | 35 +++++++++++++++++++--- src/gui/clientmanager.cpp | 56 +++++++++++++++++++++++++++++++++++ src/gui/clientmanager.h | 25 ++++++++++++++++ src/gui/main.cpp | 34 +++++++++++++++++++-- src/gui/mainwidget.cpp | 20 ++----------- src/gui/mainwidget.h | 17 +++-------- systemd/user/dbus.service | 11 ------- systemd/user/dbus.socket | 9 ------ systemd/user/qcma.service | 2 +- 17 files changed, 242 insertions(+), 122 deletions(-) delete mode 100644 systemd/user/dbus.service delete mode 100644 systemd/user/dbus.socket diff --git a/README.md b/README.md index 64949fd..5da0638 100644 --- a/README.md +++ b/README.md @@ -29,37 +29,32 @@ official CMA and also offer some features missing in the original one. ## Headless version There is a qcma_cli binary that doesn't need a X session running (useful for servers). -The daemon provides minimal interaction via dbus. Here are some usage examples: +The daemon provides minimal interaction via unix signals. Here are some usage examples: #### Send a signal to refresh the database ``` -dbus-send --session --type=method_call --dest=org.qcma.HeadlessManager /HeadlessManager org.qcma.HeadlessManager.refreshDatabase +kill -HUP $(pgrep qcma_cli) ``` #### Send a signal to stop the daemon ``` -dbus-send --session --type=method_call --dest=org.qcma.HeadlessManager /HeadlessManager org.qcma.HeadlessManager.stop -``` - -#### Receive confirmation when the database finished updating - -``` -dbus-monitor "type='signal',sender='org.qcma.HeadlessManager',member='databaseUpdated'" +kill $(pgrep qcma_cli) ``` ## Dependencies -* [Qt 4.x or 5.x](http://qt-project.org/) +* [Qt 4.x or 5.x](http://www.qt.io/) -* [VitaMTP fork](https://github.com/codestation/VitaMTP) +* [VitaMTP fork](https://github.com/codestation/vitamtp) * [FFmpeg](http://www.ffmpeg.org/) * [libnotify](http://library.gnome.org/devel/notification-spec/) -* [libappindicator](https://launchpad.net/libappindicator) +* [libappindicator (optional)](https://launchpad.net/libappindicator) +* [KNotifications (optional)](https://projects.kde.org/projects/frameworks/knotifications) #### Where do I get the source code? Check the GitHub repo here: https://github.com/codestation/qcma @@ -71,10 +66,7 @@ Contact me on [GitHub](https://github.com/codestation/) [Yifan Lu](https://github.com/yifanlu/vitamtp/) - for the vitamtp library and the reference implementation of OpenCMA. -[Xian Nox] (https://github.com/xiannox) - for the Wiki and various contributions. - -[173210] (https://github.com/173210) - japanese translations. +Other contributors: check [here](https://github.com/codestation/qcma/graphs/contributors) #### License -GPL v3: since some parts of QCMA are based on the reference implementation of -OpenCMA. +GPLv3 diff --git a/qcma.6 b/qcma.6 index 5a4120a..e9720f1 100644 --- a/qcma.6 +++ b/qcma.6 @@ -8,7 +8,7 @@ qcma \- Content manager for the PlayStation Vita \fBqcma\fR is a cross-platform application to provide an Open Source implementation of the original Content Manager Assistant that comes with the PS Vita. .TP -QCMA will let you backup your games, songs, photos & videos via USB or wireless. +Qcma will let you backup your games, songs, photos & videos via USB or wireless. .SH "SEE ALSO" .I /usr/share/doc/qcma/README.md diff --git a/qcma.pro b/qcma.pro index a16f552..a662209 100644 --- a/qcma.pro +++ b/qcma.pro @@ -12,7 +12,7 @@ android { SUBDIRS = qcma_gui.pro } -# Compile the headless binary only on Linux because it depends on dbus +# Compile the headless binary only on Linux unix:!macx:!android { SUBDIRS += qcma_cli.pro # The appindicator and kde extensions are linux only too diff --git a/qcma_cli.6 b/qcma_cli.6 index e4224a0..feb93b5 100644 --- a/qcma_cli.6 +++ b/qcma_cli.6 @@ -8,23 +8,26 @@ qcma-cli \- Content manager for the PlayStation Vita .B \-\-verbose print detailed output +.TP +.B \-\-with-debug +print very verbose output (including hexdump transfers) + .SH DESCRIPTION \fBqcma_cli\fR is a cross-platform application to provide an Open Source implementation of the original Content Manager Assistant that comes with the PS Vita. .TP -QCMA will let you backup your games, songs, photos & videos via USB or wireless. +Qcma will let you backup your games, songs, photos & videos via USB or wireless. -.SH COMMANDS -You can control qcma_cli over DBus: +.SH SIGNALS +You can control \fBqcma_cli\fR using signals, i.e. using the \fBkill\fR command to send a signal to the process. .TP -Send a signal to refresh the database: -.BR -dbus-send \-\-session \-\-type=method_call \-\-dest=org.qcma.HeadlessManager /HeadlessManager org.qcma.HeadlessManager.refreshDatabase +.B SIGHUP +Refreshes the database. + .TP -Send a signal to stop the daemon: -.BR -dbus-send \-\-session \-\-type=method_call \-\-dest=org.qcma.HeadlessManager /HeadlessManager org.qcma.HeadlessManager.stop +.B SIGTERM +Shuts down the process but waits until the current event is finished. .SH "SEE ALSO" .I /usr/share/doc/qcma/README.md diff --git a/qcma_cli.pro b/qcma_cli.pro index ce02750..86d4597 100644 --- a/qcma_cli.pro +++ b/qcma_cli.pro @@ -1,7 +1,5 @@ include(qcma_common.pri) -unix:!macx:QT += dbus - TARGET = qcma_cli SOURCES += \ @@ -19,13 +17,3 @@ unix:!macx { INSTALLS += target } - -# Create the introspection XML -QT5_SUFFIX { - system(qdbuscpp2xml-qt5 -M -s src/cli/headlessmanager.h -o org.qcma.HeadlessManager.xml) -} else { - system(qdbuscpp2xml -M -s src/cli/headlessmanager.h -o org.qcma.HeadlessManager.xml) -} - -# Create the helper class -DBUS_ADAPTORS = org.qcma.HeadlessManager.xml diff --git a/qcma_gui.pro b/qcma_gui.pro index a68221b..76fa196 100644 --- a/qcma_gui.pro +++ b/qcma_gui.pro @@ -59,15 +59,3 @@ unix:!macx { INSTALLS += target desktop icon64 } - -unix:!macx { - QT += dbus - # Create the introspection XML - QT5_SUFFIX { - system(qdbuscpp2xml-qt5 -M -s src/gui/mainwidget.h -o org.qcma.ClientManager.xml) - } else { - system(qdbuscpp2xml -M -s src/gui/mainwidget.h -o org.qcma.ClientManager.xml) - } - # Create the helper class - DBUS_ADAPTORS = org.qcma.ClientManager.xml -} diff --git a/src/cli/headlessmanager.cpp b/src/cli/headlessmanager.cpp index e31dfda..6b73f41 100644 --- a/src/cli/headlessmanager.cpp +++ b/src/cli/headlessmanager.cpp @@ -22,20 +22,29 @@ #include "sqlitedb.h" #include "qlistdb.h" #include "headlessmanager.h" -#include "headlessmanager_adaptor.h" #include #include +#include +#include #include +int HeadlessManager::sighup_fd[2]; +int HeadlessManager::sigterm_fd[2]; + HeadlessManager::HeadlessManager(QObject *obj_parent) : - QObject(obj_parent), dbus_conn(QDBusConnection::sessionBus()) + QObject(obj_parent) { - new HeadlessManagerAdaptor(this); - QDBusConnection dbus = QDBusConnection::sessionBus(); - // expose qcma over dbus so the database update can be triggered - dbus.registerObject("/HeadlessManager", this); - dbus.registerService("org.qcma.HeadlessManager"); + if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sighup_fd)) + qFatal("Couldn't create HUP socketpair"); + + if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sigterm_fd)) + qFatal("Couldn't create TERM socketpair"); + + sn_hup = new QSocketNotifier(sighup_fd[1], QSocketNotifier::Read, this); + connect(sn_hup, SIGNAL(activated(int)), this, SLOT(handleSigHup())); + sn_term = new QSocketNotifier(sigterm_fd[1], QSocketNotifier::Read, this); + connect(sn_term, SIGNAL(activated(int)), this, SLOT(handleSigTerm())); } HeadlessManager::~HeadlessManager() @@ -71,7 +80,7 @@ void HeadlessManager::start() // initializing database for the first use refreshDatabase(); - // send a signal over dbus of the connected peers knows when the update is finished + // send a signal when the update is finished connect(m_db, SIGNAL(updated(int)), this, SIGNAL(databaseUpdated(int))); thread_count = 0; @@ -134,3 +143,37 @@ void HeadlessManager::threadStopped() } mutex.unlock(); } + +void HeadlessManager::hupSignalHandler(int) +{ + char a = 1; + ::write(sighup_fd[0], &a, sizeof(a)); +} + +void HeadlessManager::termSignalHandler(int) +{ + char a = 1; + ::write(sigterm_fd[0], &a, sizeof(a)); +} + +void HeadlessManager::handleSigTerm() +{ + sn_term->setEnabled(false); + char tmp; + ::read(sigterm_fd[1], &tmp, sizeof(tmp)); + + stop(); + + sn_term->setEnabled(true); +} + +void HeadlessManager::handleSigHup() +{ + sn_hup->setEnabled(false); + char tmp; + ::read(sighup_fd[1], &tmp, sizeof(tmp)); + + refreshDatabase(); + + sn_hup->setEnabled(true); +} diff --git a/src/cli/headlessmanager.h b/src/cli/headlessmanager.h index 44c3733..39be819 100644 --- a/src/cli/headlessmanager.h +++ b/src/cli/headlessmanager.h @@ -24,12 +24,11 @@ #include #include -#include +#include class HeadlessManager : public QObject { Q_OBJECT - Q_CLASSINFO("D-Bus Interface", "org.qcma.HeadlessManager") public: explicit HeadlessManager(QObject *parent = 0); @@ -37,6 +36,10 @@ public: void start(); + // unix signal handlers + static void hupSignalHandler(int); + static void termSignalHandler(int); + private: int thread_count; QMutex mutex; @@ -45,16 +48,26 @@ private: QThread *usb_thread; QThread *wireless_thread; - QDBusConnection dbus_conn; + + // signal handling + static int sighup_fd[2]; + static int sigterm_fd[2]; + + QSocketNotifier *sn_hup; + QSocketNotifier *sn_term; signals: void stopped(); - Q_SCRIPTABLE void databaseUpdated(int count); + void databaseUpdated(int count); public slots: void refreshDatabase(); void stop(); + // Qt signal handlers + void handleSigHup(); + void handleSigTerm(); + private slots: void threadStopped(); }; diff --git a/src/cli/main_cli.cpp b/src/cli/main_cli.cpp index 34f9633..75a21b5 100644 --- a/src/cli/main_cli.cpp +++ b/src/cli/main_cli.cpp @@ -47,20 +47,47 @@ static void noMessageOutput(QtMsgType type, const char *msg) Q_UNUSED(msg); } +static bool setup_handlers() +{ + struct sigaction hup, term; + + hup.sa_handler = HeadlessManager::hupSignalHandler; + sigemptyset(&hup.sa_mask); + hup.sa_flags = 0; + hup.sa_flags |= SA_RESTART; + + if (sigaction(SIGHUP, &hup, NULL) != 0) { + qCritical("SIGHUP signal handle failed"); + return false; + } + + term.sa_handler = HeadlessManager::termSignalHandler; + sigemptyset(&term.sa_mask); + term.sa_flags |= SA_RESTART; + + if (sigaction(SIGTERM, &term, NULL) != 0) { + qCritical("SIGTERM signal handle failed"); + return false; + } + + return true; +} + int main(int argc, char *argv[]) { - if(SingleCoreApplication::sendMessage(QObject::tr("A instance of QCMA is already running"))) { + if(SingleCoreApplication::sendMessage(QObject::tr("An instance of Qcma is already running"))) { return 0; } SingleCoreApplication app(argc, argv); -#ifndef Q_OS_WIN32 // FIXME: libmtp sends SIGPIPE if a socket write fails crashing the whole app // the proper fix is to libmtp to handle the cancel properly or ignoring // SIGPIPE on the socket signal(SIGPIPE, SIG_IGN); -#endif + + if(!setup_handlers()) + return 1; if(app.arguments().contains("--with-debug")) { VitaMTP_Set_Logging(VitaMTP_DEBUG); @@ -79,7 +106,7 @@ int main(int argc, char *argv[]) QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8")); #endif - qDebug("Starting QCMA %s", QCMA_VER); + QTextStream(stdout) << "Starting Qcma " << QCMA_VER << endl; QTranslator translator; QString locale = QLocale().system().name(); diff --git a/src/gui/clientmanager.cpp b/src/gui/clientmanager.cpp index 00a0388..3658d34 100644 --- a/src/gui/clientmanager.cpp +++ b/src/gui/clientmanager.cpp @@ -26,9 +26,29 @@ #include +#ifdef Q_OS_UNIX +#include +#include + +int ClientManager::sighup_fd[2]; +int ClientManager::sigterm_fd[2]; +#endif + ClientManager::ClientManager(Database *db, QObject *obj_parent) : QObject(obj_parent), m_db(db) { +#ifdef Q_OS_UNIX + if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sighup_fd)) + qFatal("Couldn't create HUP socketpair"); + + if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sigterm_fd)) + qFatal("Couldn't create TERM socketpair"); + + sn_hup = new QSocketNotifier(sighup_fd[1], QSocketNotifier::Read, this); + connect(sn_hup, SIGNAL(activated(int)), this, SLOT(handleSigHup())); + sn_term = new QSocketNotifier(sigterm_fd[1], QSocketNotifier::Read, this); + connect(sn_term, SIGNAL(activated(int)), this, SLOT(handleSigTerm())); +#endif } ClientManager::~ClientManager() @@ -150,3 +170,39 @@ void ClientManager::threadStopped() } mutex.unlock(); } + +#ifdef Q_OS_UNIX +void ClientManager::hupSignalHandler(int) +{ + char a = 1; + ::write(sighup_fd[0], &a, sizeof(a)); +} + +void ClientManager::termSignalHandler(int) +{ + char a = 1; + ::write(sigterm_fd[0], &a, sizeof(a)); +} + +void ClientManager::handleSigTerm() +{ + sn_term->setEnabled(false); + char tmp; + ::read(sigterm_fd[1], &tmp, sizeof(tmp)); + + stop(); + + sn_term->setEnabled(true); +} + +void ClientManager::handleSigHup() +{ + sn_hup->setEnabled(false); + char tmp; + ::read(sighup_fd[1], &tmp, sizeof(tmp)); + + refreshDatabase(); + + sn_hup->setEnabled(true); +} +#endif diff --git a/src/gui/clientmanager.h b/src/gui/clientmanager.h index a6af841..07acee1 100644 --- a/src/gui/clientmanager.h +++ b/src/gui/clientmanager.h @@ -27,6 +27,10 @@ #include #include +#ifdef Q_OS_UNIX +#include +#endif + class ClientManager : public QObject { Q_OBJECT @@ -37,6 +41,12 @@ public: void start(); void stop(); +#ifdef Q_OS_UNIX + // unix signal handlers + static void hupSignalHandler(int); + static void termSignalHandler(int); +#endif + private: int thread_count; QMutex mutex; @@ -49,6 +59,15 @@ private: QThread *usb_thread; QThread *wireless_thread; +#ifdef Q_OS_UNIX + // signal handling + static int sighup_fd[2]; + static int sigterm_fd[2]; + + QSocketNotifier *sn_hup; + QSocketNotifier *sn_term; +#endif + signals: void updated(int); void stopped(); @@ -60,6 +79,12 @@ signals: public slots: void refreshDatabase(); +#ifdef Q_OS_UNIX + // Qt signal handlers + void handleSigHup(); + void handleSigTerm(); +#endif + private slots: void threadStopped(); void databaseUpdated(int count); diff --git a/src/gui/main.cpp b/src/gui/main.cpp index 5a5231b..3e0efe2 100644 --- a/src/gui/main.cpp +++ b/src/gui/main.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -45,9 +46,36 @@ static void noMessageOutput(QtMsgType type, const char *msg) Q_UNUSED(msg); } + +static bool setup_handlers() +{ + struct sigaction hup, term; + + hup.sa_handler = ClientManager::hupSignalHandler; + sigemptyset(&hup.sa_mask); + hup.sa_flags = 0; + hup.sa_flags |= SA_RESTART; + + if (sigaction(SIGHUP, &hup, NULL) != 0) { + qCritical("SIGHUP signal handle failed"); + return false; + } + + term.sa_handler = ClientManager::termSignalHandler; + sigemptyset(&term.sa_mask); + term.sa_flags |= SA_RESTART; + + if (sigaction(SIGTERM, &term, NULL) != 0) { + qCritical("SIGTERM signal handle failed"); + return false; + } + + return true; +} + int main(int argc, char *argv[]) { - if(SingleApplication::sendMessage(QObject::tr("A instance of QCMA is already running"))) { + if(SingleApplication::sendMessage(QObject::tr("An instance of Qcma is already running"))) { return 0; } @@ -60,6 +88,8 @@ int main(int argc, char *argv[]) signal(SIGPIPE, SIG_IGN); #endif + setup_handlers(); + if(app.arguments().contains("--with-debug")) { VitaMTP_Set_Logging(VitaMTP_DEBUG); } else if(app.arguments().contains("--verbose")) { @@ -77,7 +107,7 @@ int main(int argc, char *argv[]) QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8")); #endif - qDebug("Starting QCMA %s", QCMA_VER); + QTextStream(stdout) << "Starting Qcma " << QCMA_VER << endl; QTranslator translator; QString locale = QLocale().system().name(); diff --git a/src/gui/mainwidget.cpp b/src/gui/mainwidget.cpp index 85e1c31..109eb77 100644 --- a/src/gui/mainwidget.cpp +++ b/src/gui/mainwidget.cpp @@ -21,10 +21,6 @@ #include "cmaclient.h" #include "cmautils.h" -#ifdef Q_OS_LINUX -#include "clientmanager_adaptor.h" -#endif - #include "qlistdb.h" #include "sqlitedb.h" @@ -34,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -44,24 +41,11 @@ const QStringList MainWidget::path_list = QStringList() << "photoPath" << "music bool sleptOnce = false; -#ifdef Q_OS_LINUX MainWidget::MainWidget(QWidget *obj_parent) : - QWidget(obj_parent), db(NULL), configForm(NULL), managerForm(NULL), backupForm(NULL), dbus_conn(QDBusConnection::sessionBus()) -{ - new ClientManagerAdaptor(this); - QDBusConnection dbus = QDBusConnection::sessionBus(); - // expose qcma over dbus so the database update can be triggered - dbus.registerObject("/ClientManager", this); - dbus.registerService("org.qcma.ClientManager"); - trayIcon = NULL; -} -#else -MainWidget::MainWidget(QWidget *parent) : - QWidget(parent), db(NULL), configForm(NULL), managerForm(NULL), backupForm(NULL) + QWidget(obj_parent), db(NULL), configForm(NULL), managerForm(NULL), backupForm(NULL) { trayIcon = NULL; } -#endif void MainWidget::checkSettings() { diff --git a/src/gui/mainwidget.h b/src/gui/mainwidget.h index b290c51..4b40775 100644 --- a/src/gui/mainwidget.h +++ b/src/gui/mainwidget.h @@ -31,10 +31,6 @@ #include -#ifdef Q_OS_LINUX -#include -#endif - #include class TrayIndicator; @@ -42,7 +38,6 @@ class TrayIndicator; class MainWidget : public QWidget { Q_OBJECT - Q_CLASSINFO("D-Bus Interface", "org.qcma.ClientManager") public: explicit MainWidget(QWidget *parent = 0); @@ -66,19 +61,15 @@ private: ClientManager *managerForm; BackupManagerForm *backupForm; -#ifdef Q_OS_LINUX - QDBusConnection dbus_conn; -#endif - TrayIndicator *trayIcon; const static QStringList path_list; signals: - Q_SCRIPTABLE void deviceConnected(QString); - Q_SCRIPTABLE void deviceDisconnected(); - Q_SCRIPTABLE void databaseUpdated(int count); - Q_SCRIPTABLE void messageReceived(QString message); + void deviceConnected(QString); + void deviceDisconnected(); + void databaseUpdated(int count); + void messageReceived(QString message); public slots: void openConfig(); diff --git a/systemd/user/dbus.service b/systemd/user/dbus.service deleted file mode 100644 index 9e085a0..0000000 --- a/systemd/user/dbus.service +++ /dev/null @@ -1,11 +0,0 @@ -[Unit] -Description=D-Bus User Message Bus -Documentation=man:dbus-daemon(1) -Requires=dbus.socket - -[Service] -ExecStart=/usr/bin/dbus-daemon --session --address=systemd: --nofork --systemd-activation -ExecReload=/usr/bin/dbus-send --print-reply --session --type=method_call --dest=org.freedesktop.DBus / org.freedesktop.DBus.ReloadConfig - -[Install] -Also=dbus.socket diff --git a/systemd/user/dbus.socket b/systemd/user/dbus.socket deleted file mode 100644 index 77958f7..0000000 --- a/systemd/user/dbus.socket +++ /dev/null @@ -1,9 +0,0 @@ -[Unit] -Description=D-Bus User Message Bus Socket - -[Socket] -ListenStream=%t/bus - -[Install] -WantedBy=sockets.target -Also=dbus.service diff --git a/systemd/user/qcma.service b/systemd/user/qcma.service index 701c737..e946076 100644 --- a/systemd/user/qcma.service +++ b/systemd/user/qcma.service @@ -5,7 +5,7 @@ ConditionPathExists=%h/.config/codestation/qcma.conf [Service] ExecStart=/usr/bin/qcma_cli --verbose -ExecStop=/usr/bin/dbus-send --print-reply --session --type=method_call --dest=org.qcma.HeadlessManager /HeadlessManager org.qcma.HeadlessManager.stop +ExecReload=/bin/kill -HUP $MAINPID [Install] Alias=qcma_cli.service