// rdselect_help.cpp // // SETUID helper script for rdselect(1) // // (C) Copyright 2018 Fred Gleason // // 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 #include #include #include #include #include #include #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); QString options="-fstype="+helper_config->audioStoreMountType(); if(!helper_config->audioStoreMountOptions().isEmpty()) { options+=","+helper_config->audioStoreMountOptions(); } fprintf(f,"%s\t%s\t%s\n", (const char *)helper_config->audioRoot().toUtf8(), (const char *)options.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