Files
cantata/devices/mounter/mounter.cpp
Craig Drummond 0a73215867 Update (c) year
2017-04-02 18:45:30 +01:00

248 lines
7.5 KiB
C++

/*
* Cantata
*
* Copyright (c) 2011-2017 Craig Drummond <craig.p.drummond@gmail.com>
*
* ----
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "mounter.h"
#include "mounteradaptor.h"
#include "config.h"
#include <stdlib.h>
#include <QCoreApplication>
#include <QUrl>
#if QT_VERSION >= 0x050000
#include <QUrlQuery>
#endif
#include <QProcess>
#include <QTemporaryFile>
#include <QTextStream>
#include <QTimer>
#include <QDebug>
#include <sys/types.h>
#include <signal.h>
Mounter::Mounter(QObject *p)
: QObject(p)
, timer(0)
, procCount(0)
{
new MounterAdaptor(this);
QDBusConnection bus=QDBusConnection::systemBus();
if (!bus.registerService("mpd.cantata.mounter") || !bus.registerObject("/Mounter", this)) {
QTimer::singleShot(0, qApp, SLOT(quit()));
}
}
static inline bool mpOk(const QString &mp)
{
return !mp.isEmpty() && mp.startsWith("/home/"); // ) && mp.contains("cantata");
}
static QString fixPath(const QString &dir)
{
QString d(dir);
if (!d.isEmpty() && !d.startsWith(QLatin1String("http://"))) {
d.replace(QLatin1String("//"), QChar('/'));
}
d.replace(QLatin1String("/./"), QChar('/'));
if (!d.isEmpty() && !d.endsWith('/')) {
d+='/';
}
return d;
}
// Control via:
// qdbus --system mpd.cantata.mounter /Mounter mpd.cantata.mounter.mount smb://workgroup\user:password@host:port/path?domain=domain mountPoint uid gid
void Mounter::mount(const QString &url, const QString &mountPoint, int uid, int gid, int pid)
{
if (calledFromDBus()) {
registerPid(pid);
}
qWarning() << url << mountPoint << uid << gid;
QUrl u(url);
int st=-1;
if (u.scheme()=="smb" && mpOk(mountPoint)) {
QString user=u.userName();
QString domain;
QString password=u.password();
int port=u.port();
#if QT_VERSION < 0x050000
if (u.hasQueryItem("domain")) {
domain=u.queryItemValue("domain");
}
#else
QUrlQuery q(u);
if (q.hasQueryItem("domain")) {
domain=q.queryItemValue("domain");
}
#endif
QTemporaryFile *temp=0;
if (!password.isEmpty()) {
temp=new QTemporaryFile();
if (temp && temp->open()) {
QTextStream str(temp);
if (!user.isEmpty()) {
str << "username=" << user << endl;
}
str << "password=" << password << endl;
if (!domain.isEmpty()) {
str << "domain=" << domain << endl;
}
}
}
QString path=fixPath(u.host()+"/"+u.path());
while (!path.startsWith("//")) {
path="/"+path;
}
// qWarning() << "EXEC" << "mount.cifs" << path << mountPoint
// << "-o" <<
// (temp ? ("credentials="+temp->fileName()+",") : QString())+
// "uid="+QString::number(uid)+",gid="+QString::number(gid)+
// (445==port || port<1 ? QString() : ",port="+QString::number(port))+
// (temp || user.isEmpty() ? QString() : (",username="+user))+
// (temp || domain.isEmpty() ? QString() : (",domain="+domain))+
// (temp ? QString() : ",password=");
QProcess *proc=new QProcess(this);
connect(proc, SIGNAL(finished(int)), SLOT(mountResult(int)));
proc->setProperty("mp", mountPoint);
proc->setProperty("pid", pid);
proc->start(QLatin1String(INSTALL_PREFIX"/share/cantata/scripts/mount.cifs.wrapper"),
QStringList() << path << mountPoint
<< "-o" <<
(temp ? ("credentials="+temp->fileName()+",") : QString())+
"uid="+QString::number(uid)+",gid="+QString::number(gid)+
(445==port || port<1 ? QString() : ",port="+QString::number(port))+
(temp || user.isEmpty() ? QString() : (",username="+user))+
(temp || domain.isEmpty() ? QString() : (",domain="+domain))+
(temp ? QString() : ",password="), QIODevice::WriteOnly);
if (temp) {
tempFiles.insert(proc, temp);
}
procCount++;
return;
}
emit mountStatus(mountPoint, pid, st);
}
// Control via:
// qdbus --system mpd.cantata.mounter /Mounter mpd.cantata.mounter.umount mountPoint
void Mounter::umount(const QString &mountPoint, int pid)
{
if (calledFromDBus()) {
registerPid(pid);
}
if (mpOk(mountPoint)) {
QProcess *proc=new QProcess(this);
connect(proc, SIGNAL(finished(int)), SLOT(umountResult(int)));
proc->start("umount", QStringList() << mountPoint);
proc->setProperty("mp", mountPoint);
proc->setProperty("pid", pid);
procCount++;
} else {
emit umountStatus(mountPoint, pid, -1);
}
}
void Mounter::mountResult(int st)
{
QProcess *proc=dynamic_cast<QProcess *>(sender());
qWarning() << "MOUNT RESULT" << st << (void *)proc;
if (proc) {
procCount--;
proc->close();
proc->deleteLater();
if (tempFiles.contains(proc)) {
tempFiles[proc]->close();
tempFiles[proc]->deleteLater();
tempFiles.remove(proc);
}
emit mountStatus(proc->property("mp").toString(), proc->property("pid").toInt(), st);
}
startTimer();
}
void Mounter::umountResult(int st)
{
QProcess *proc=dynamic_cast<QProcess *>(sender());
if (proc) {
procCount--;
proc->close();
proc->deleteLater();
emit umountStatus(proc->property("mp").toString(), proc->property("pid").toInt(), st);
}
startTimer();
}
void Mounter::startTimer()
{
if (!timer) {
timer=new QTimer(this);
connect(timer, SIGNAL(timeout()), SLOT(timeout()));
}
timer->start(30000);
}
void Mounter::registerPid(int pid)
{
pids.insert(pid);
startTimer();
}
void Mounter::timeout()
{
if (procCount!=0) {
startTimer();
return;
}
QSet<int> running;
foreach (int p, pids) {
if (0==kill(p, 0)) {
running.insert(p);
}
}
pids=running;
if (pids.isEmpty()) {
qApp->exit();
QMap<QObject *, QTemporaryFile *>::ConstIterator it(tempFiles.constBegin());
QMap<QObject *, QTemporaryFile *>::ConstIterator end(tempFiles.constEnd());
for (; it!=end; ++it) {
it.value()->close();
delete it.value();
}
tempFiles.clear();
} else {
startTimer();
}
}