Rivendellaudio/utils/rdselect_helper/rdselect_helper.cpp
Fred Gleason f9e0e152ba 2018-11-01 Fred Gleason <fredg@paravelsystems.com>
* Modified rdselect_helper(8) to use the automounter for managing
	audio store mounts.
2018-11-01 13:14:49 -04:00

302 lines
7.4 KiB
C++

// rdselect_help.cpp
//
// SETUID helper script for rdselect(1)
//
// (C) Copyright 2018 Fred Gleason <fredg@paravelsystems.com>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
//
// 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; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#include <errno.h>
#include <unistd.h>
#include <sys/mount.h>
#include <sys/types.h>
#include <qcoreapplication.h>
#include <qdir.h>
#include <qprocess.h>
#include "rd.h"
#include "rdselect_helper.h"
MainObject::MainObject(QObject *parent)
: QObject(parent)
{
setuid(geteuid()); // So the SETUID bit works as expected
if(getuid()!=0) {
fprintf(stderr,
"rdselect_helper: this program must be installed SETUID root\n");
exit(RDConfig::RDSelectNotRoot);
}
//
// Process argument
//
if(qApp->argc()!=2) {
fprintf(stderr,"rdselect_helper: you must pass exactly one argument\n");
exit(RDConfig::RDSelectInvalidArguments);
}
if(QString(qApp->argv()[1]).contains("/")||
QString(qApp->argv()[1]).contains("..")) {
fprintf(stderr,"rdselect_helper: invalid configuration name\n");
exit(RDConfig::RDSelectInvalidName);
}
helper_config_filename=
QString(RD_DEFAULT_RDSELECT_DIR)+"/"+QString(qApp->argv()[1]);
//
// Load Configurations
//
helper_config=new RDConfig();
helper_config->setFilename(helper_config_filename);
if(!helper_config->load()) {
fprintf(stderr,"rdselect_helper: no such configuration\n");
exit(RDConfig::RDSelectNoSuchConfiguration);
}
helper_prev_config=new RDConfig();
if(!helper_prev_config->load()) {
fprintf(stderr,"rdselect_helper: no current configuration found\n");
exit(RDConfig::RDSelectNoCurrentConfig);
}
//
// Check system state
//
if(ModulesActive()) {
fprintf(stderr,"rdselect_helper: one or more rivendell modules active\n");
exit(RDConfig::RDSelectModulesActive);
}
//
// Shutdown old setup
//
Shutdown();
//
// Startup new setup
//
Startup();
exit(RDConfig::RDSelectOk);
}
void MainObject::Startup()
{
QStringList args;
QProcess *proc=NULL;
FILE *f=NULL;
if(!helper_config->audioStoreMountSource().isEmpty()) {
//
// Stop the Automounter
//
ControlAutomounter("stop");
//
// Update the Automounter
//
if((f=fopen(RDSELECT_AUTOMOUNT_CONFIG,"w"))==NULL) {
fprintf(stderr,
"rdselect_helper: unable to open automount configuration \"%s\" [%s]\n",
RDSELECT_AUTOMOUNT_CONFIG,
strerror(errno));
exit(RDConfig::RDSelectCantAccessAutomount);
}
fprintf(f,"%s",RDSELECT_AUTOMOUNT_WARNING);
fprintf(f,"%s\t-fstype=%s\t%s\n",
(const char *)helper_config->audioRoot().toUtf8(),
(const char *)helper_config->audioStoreMountType().toUtf8(),
(const char *)helper_config->audioStoreMountSource().toUtf8());
fclose(f);
//
// Restart the Automounter
//
ControlAutomounter("start");
}
//
// Start the rivendell service
//
unlink(RD_CONF_FILE);
if(symlink(helper_config_filename,RD_CONF_FILE)!=0) {
fprintf(stderr,"rdselect_helper: unable to create new symlink [%s]\n",
strerror(errno));
exit(RDConfig::RDSelectSymlinkFailed);
}
args.clear();
args.push_back("start");
args.push_back("rivendell");
proc=new QProcess(this);
proc->start("/bin/systemctl",args);
proc->waitForFinished();
if(proc->exitStatus()!=QProcess::NormalExit) {
fprintf(stderr,"rdselect_helper: systemctl(8) crashed\n");
exit(RDConfig::RDSelectSystemctlCrashed);
}
if(proc->exitCode()!=0) {
fprintf(stderr,"rdselect_helper: rivendell service start failed\n");
exit(RDConfig::RDSelectRivendellStartupFailed);
}
delete proc;
}
void MainObject::Shutdown()
{
QStringList args;
FILE *f=NULL;
//
// Stop Rivendell Service
//
args.push_back("stop");
args.push_back("rivendell");
QProcess *proc=new QProcess(this);
proc->start("/bin/systemctl",args);
proc->waitForFinished();
if(proc->exitStatus()!=QProcess::NormalExit) {
fprintf(stderr,"rdselect_helper: systemctl(8) crashed\n");
exit(RDConfig::RDSelectSystemctlCrashed);
}
if(proc->exitCode()!=0) {
fprintf(stderr,"rdselect_helper: rivendell service shutdown failed\n");
exit(RDConfig::RDSelectRivendellShutdownFailed);
}
delete proc;
//
// Stop the Automounter
//
ControlAutomounter("stop");
//
// Unmount the audio store
//
if(umount(helper_prev_config->audioRoot())!=0) {
if(errno!=EINVAL) { // Ignore the error if we're already unmounted
fprintf(stderr,"rdselect_helper: unmount of \"%s\" failed [%s]\n",
(const char *)helper_prev_config->audioRoot(),
strerror(errno));
exit(RDConfig::RDSelectAudioUnmountFailed);
}
}
//
// Update the Automounter
//
if((f=fopen(RDSELECT_AUTOMOUNT_CONFIG,"w"))==NULL) {
fprintf(stderr,
"rdselect_helper: unable to open automount configuration \"%s\" [%s]\n",
RDSELECT_AUTOMOUNT_CONFIG,
strerror(errno));
exit(RDConfig::RDSelectCantAccessAutomount);
}
fprintf(f,"%s",RDSELECT_AUTOMOUNT_WARNING);
fclose(f);
//
// Restart the Automounter
//
ControlAutomounter("start");
}
void MainObject::ControlAutomounter(const QString &cmd)
{
QStringList args;
args.push_back(cmd);
args.push_back("autofs");
QProcess *proc=new QProcess(this);
proc->start("/bin/systemctl",args);
proc->waitForFinished();
if(proc->exitStatus()!=QProcess::NormalExit) {
fprintf(stderr,"rdselect_helper: systemctl(8) crashed\n");
exit(RDConfig::RDSelectSystemctlCrashed);
}
if(proc->exitCode()!=0) {
fprintf(stderr,
"rdselect_helper: automounter control failed [systemctl exit code: %d]\n",
proc->exitCode());
}
delete proc;
}
bool MainObject::ProcessActive(const QStringList &cmds) const
{
QStringList dirs;
QDir *proc_dir=new QDir("/proc");
bool ok=false;
FILE *f=NULL;
char line[1024];
QString cmdline;
proc_dir->setFilter(QDir::Dirs);
dirs=proc_dir->entryList();
for(int i=0;i<dirs.size();i++) {
dirs[i].toInt(&ok);
if(ok) {
if((f=fopen(QString("/proc/")+dirs[i]+"/cmdline","r"))!=NULL) {
if(fgets(line,1024,f)!=NULL) {
QStringList f1=QString(line).split(" ",QString::SkipEmptyParts);
QStringList f2=f1[0].split("/",QString::SkipEmptyParts);
cmdline=f2[f2.size()-1];
for(int j=0;j<cmds.size();j++) {
if(cmdline==cmds[j]) {
fclose(f);
return true;
}
}
}
fclose(f);
}
}
}
delete proc_dir;
return false;
}
bool MainObject::ModulesActive() const
{
QStringList cmds;
cmds.push_back("rdadmin");
cmds.push_back("rdairplay");
cmds.push_back("rdcartslots");
cmds.push_back("rdcastmanager");
cmds.push_back("rdcatch");
cmds.push_back("rdlibrary");
cmds.push_back("rdlogedit");
cmds.push_back("rdlogin");
cmds.push_back("rdlogmanager");
cmds.push_back("rdpanel");
cmds.push_back("rddbmgr");
cmds.push_back("rdgpimon");
return ProcessActive(cmds);
}
int main(int argc,char *argv[])
{
QCoreApplication a(argc,argv);
new MainObject();
return a.exec();
}