fabric/executils.cpp

fabric/executils.cpp

fabric/executils.cpp

Functions

Name
QStringshellQuote(const QString & str)
Shell-escape a string.
QStringfindHostExecutable(const QString & exe)
Find executable on the host system (if running in a sandbox)
intrunHostExecutable(const QString & exe, const QStringList & args, bool waitForFinished)
Run command on the host.
intrunInExternalTerminal(const QString & cmd, const QStringList & args, const QString & wdir)

Functions Documentation

function shellQuote

QString shellQuote(
    const QString & str
)

Shell-escape a string.

function findHostExecutable

QString findHostExecutable(
    const QString & exe
)

Find executable on the host system (if running in a sandbox)

Parameters:

  • exe Name of the executable to look for

Return: Executable name, or empty string if not found

function runHostExecutable

int runHostExecutable(
    const QString & exe,
    const QStringList & args,
    bool waitForFinished
)

Run command on the host.

Parameters:

  • exe The program to run
  • args Program arguments
  • waitForFinished Wait for the command to finish

Return: Exit status of the program (if waiting for finished)

function runInExternalTerminal

int runInExternalTerminal(
    const QString & cmd,
    const QStringList & args,
    const QString & wdir
)

Source code

/*
 * Copyright (C) 2016-2024 Matthias Klumpp <matthias@tenstral.net>
 *
 * Licensed under the GNU Lesser General Public License Version 3
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the license, or
 * (at your option) any later version.
 *
 * This software 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this software.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "executils.h"

#include <QDebug>
#include <QFile>
#include <QFileInfo>
#include <QProcess>
#include <QStandardPaths>
#include <glib.h>

#include "sysinfo.h"
#include "utils/misc.h"

using namespace Syntalos;

QString shellQuote(const QString &str)
{
    auto cs = g_shell_quote(qPrintable(str));
    const auto res = QString::fromUtf8(cs);
    g_free(cs);
    return res;
}

QString findHostExecutable(const QString &exe)
{
    auto sysInfo = SysInfo::get();
    if (sysInfo->inFlatpakSandbox()) {
        QStringList exeLocations = QStringList() << "/usr/bin"
                                                 << "/usr/local/bin"
                                                 << "/usr/sbin"
                                                 << "";
        for (const auto &loc : exeLocations) {
            QString exeHost = QStringLiteral("/run/host%1/%2").arg(loc, exe);
            QFileInfo fi(exeHost);
            if (fi.isExecutable())
                return loc + "/" + exe;
        }
        return QString();
    }

    // no sandbox
    return QStandardPaths::findExecutable(exe);
}

int runHostExecutable(const QString &exe, const QStringList &args, bool waitForFinished)
{
    auto sysInfo = SysInfo::get();
    QProcess proc;
    proc.setProcessChannelMode(QProcess::ForwardedChannels);
    if (sysInfo->inFlatpakSandbox()) {
        // in sandbox, go via flatpak-spawn
        QStringList fpsArgs = QStringList() << "--host";
        fpsArgs << exe;
        if (waitForFinished)
            proc.start("flatpak-spawn", fpsArgs + args);
        else
            proc.startDetached("flatpak-spawn", fpsArgs + args);
    } else {
        // no sandbox, we can run the command directly
        if (waitForFinished)
            proc.start(exe, args);
        else
            proc.startDetached(exe, args);
    }
    if (waitForFinished) {
        proc.waitForFinished(-1);
        return proc.exitCode();
    } else {
        return 0;
    }
}

int runInExternalTerminal(const QString &cmd, const QStringList &args, const QString &wdir)
{
    QString terminalExe;
    QStringList extraTermArgs;
    auto sysInfo = SysInfo::get();

    if (terminalExe.isEmpty()) {
        terminalExe = findHostExecutable("konsole");
        extraTermArgs.append("--hide-menubar");
    }
    if (terminalExe.isEmpty()) {
        terminalExe = findHostExecutable("gnome-terminal");
        extraTermArgs.append("--hide-menubar");
    }
    if (terminalExe.isEmpty())
        terminalExe = findHostExecutable("xterm");
    if (terminalExe.isEmpty())
        terminalExe = findHostExecutable("x-terminal-emulator");

    if (terminalExe.isEmpty())
        return -255;

    auto rtdDir = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);
    if (rtdDir.isEmpty())
        rtdDir = "/tmp";
    auto exitFname = QStringLiteral("%1/sy-termexit-%2").arg(rtdDir, createRandomString(6));
    auto shHelperFname = QStringLiteral("%1/sy-termrun-%2").arg(rtdDir, createRandomString(6));

    QFile shFile(shHelperFname);
    if (!shFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
        qWarning().noquote() << "Unable to open temporary file" << shHelperFname << "for writing.";
        return -255;
    }

    QString cmdShell = shellQuote(cmd);
    for (const auto &a : args)
        cmdShell += QStringLiteral(" %1").arg(shellQuote(a));
    QTextStream out(&shFile);
    out << "#!/bin/sh\n" << cmdShell << "\n" << QStringLiteral("echo $? > %1\n").arg(exitFname);
    shFile.flush();
    shFile.setPermissions(QFileDevice::ExeUser | QFileDevice::ReadUser | QFileDevice::WriteUser);
    shFile.close();

    QProcess proc;
    proc.setProcessChannelMode(QProcess::ForwardedChannels);
    if (sysInfo->inFlatpakSandbox()) {
        // in sandbox, go via flatpak-spawn
        QStringList fpsArgs = QStringList() << "--host";
        if (!wdir.isEmpty())
            fpsArgs << "--directory=" + wdir;
        fpsArgs << terminalExe;

        const auto termArgs = QStringList() << "-e"
                                            << "flatpak enter " + sysInfo->sandboxAppId() + " sh -c " + shHelperFname;
        proc.start("flatpak-spawn", fpsArgs + extraTermArgs + termArgs);
    } else {
        // no sandbox, we can run the command directly
        const auto termArgs = QStringList() << "-e"
                                            << "sh"
                                            << "-c" << shHelperFname;
        if (!wdir.isEmpty())
            proc.setWorkingDirectory(wdir);

        proc.start(terminalExe, extraTermArgs + termArgs);
    }
    proc.waitForFinished(-1);
    shFile.remove();

    // the terminal itself failed, so the command can't have worked
    if (proc.exitStatus() != 0) {
        QFile::remove(exitFname);
        return proc.exitStatus();
    }

    QFile file(exitFname);
    if (!file.open(QIODevice::ReadWrite | QIODevice::Text))
        return -255;

    int ret = QString::fromUtf8(file.readAll()).toInt();
    file.remove();
    return ret;
}

Updated on 2024-09-05 at 17:39:59 +0000