File indexing completed on 2024-04-21 05:45:46

0001 /*******************************************************************************
0002  * Copyright (C) 2008-2013 Konstantinos Smanis <konstantinos.smanis@gmail.com> *
0003  *                                                                             *
0004  * This program is free software: you can redistribute it and/or modify it     *
0005  * under the terms of the GNU General Public License as published by the Free  *
0006  * Software Foundation, either version 3 of the License, or (at your option)   *
0007  * any later version.                                                          *
0008  *                                                                             *
0009  * This program is distributed in the hope that it will be useful, but WITHOUT *
0010  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or       *
0011  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for    *
0012  * more details.                                                               *
0013  *                                                                             *
0014  * You should have received a copy of the GNU General Public License along     *
0015  * with this program. If not, see <http://www.gnu.org/licenses/>.              *
0016  *******************************************************************************/
0017 
0018 //Krazy
0019 //krazy:excludeall=cpp
0020 
0021 //Own
0022 #include "helper.h"
0023 
0024 //Qt
0025 #include <QDebug>
0026 #include <QDir>
0027 
0028 //KDE
0029 #include <KLocalizedString>
0030 #include <KProcess>
0031 #include <KAuth/HelperSupport>
0032 
0033 //Project
0034 #include "../common.h"
0035 #include "../config.h"
0036 #if HAVE_HD
0037 #undef slots
0038 #include <hd.h>
0039 #endif
0040 
0041 //The $PATH environment variable is emptied by D-Bus activation,
0042 //so let's provide a sane default. Needed for os-prober to work.
0043 static const QLatin1String path("/usr/sbin:/usr/bin:/sbin:/bin");
0044 
0045 Helper::Helper()
0046 {
0047     qputenv("PATH", path.latin1());
0048 }
0049 
0050 ActionReply Helper::executeCommand(const QStringList &command)
0051 {
0052     KProcess process;
0053     process.setProgram(command);
0054     process.setOutputChannelMode(KProcess::MergedChannels);
0055 
0056     qDebug() << "Executing" << command.join(QLatin1String(" "));
0057     int exitCode = process.execute();
0058     const QByteArray output = process.readAll();
0059 
0060     ActionReply reply;
0061     if (exitCode != 0) {
0062         reply = ActionReply::HelperErrorReply(exitCode);
0063         QString errorMessage;
0064         switch (exitCode) {
0065         case -2:
0066             errorMessage = i18nc("@info", "The process could not be started.");
0067             break;
0068         case -1:
0069             errorMessage = i18nc("@info", "The process crashed.");
0070             break;
0071         default:
0072             errorMessage = QString::fromUtf8(output);
0073             break;
0074         }
0075         reply.setErrorDescription(i18nc("@info", "Command: <command>%1</command><nl/>", command.join(QLatin1String(" "))) +
0076                                   errorDescription(exitCode, errorMessage));
0077     } else {
0078         reply.addData(QStringLiteral("command"), command);
0079         reply.addData(QStringLiteral("output"), output);
0080     }
0081     return reply;
0082 }
0083 bool Helper::setLang(const QString &lang)
0084 {
0085     QFile file(grubMenuPath());
0086     if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
0087         qCritical() << "Failed to open file for reading:" << grubMenuPath();
0088         qCritical() << "Error code:" << file.error();
0089         qCritical() << "Error description:" << file.errorString();
0090         return false;
0091     }
0092     QString fileContents = QString::fromUtf8(file.readAll().constData());
0093     file.close();
0094     if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
0095         qCritical() << "Failed to open file for writing:" << grubMenuPath();
0096         qCritical() << "Error code:" << file.error();
0097         qCritical() << "Error description:" << file.errorString();
0098         return false;
0099     }
0100     fileContents.replace(QRegExp(QStringLiteral("(\\n\\s*set\\s+lang=)\\S*\\n")), QStringLiteral("\\1%1\n").arg(lang));
0101     if (file.write(fileContents.toUtf8()) == -1) {
0102         qCritical() << "Failed to write data to file:" << grubMenuPath();
0103         qCritical() << "Error code:" << file.error();
0104         qCritical() << "Error description:" << file.errorString();
0105         return false;
0106     }
0107     file.close();
0108     return true;
0109 }
0110 
0111 ActionReply Helper::defaults(QVariantMap args)
0112 {
0113     Q_UNUSED(args)
0114     ActionReply reply;
0115     QString configFileName = grubConfigPath();
0116     QString originalConfigFileName = configFileName + QLatin1String(".original");
0117 
0118     if (!QFile::exists(originalConfigFileName)) {
0119         reply = ActionReply::HelperErrorReply();
0120         reply.setError(1);
0121         reply.setErrorDescription(errorDescription(reply.errorCode(), i18n("Original configuration file <filename>%1</filename> does not exist.", originalConfigFileName)));
0122         return reply;
0123     }
0124     if (!QFile::remove(configFileName)) {
0125         reply = ActionReply::HelperErrorReply();
0126         reply.setError(2);
0127         reply.setErrorDescription(errorDescription(reply.errorCode(), i18n("Cannot remove current configuration file <filename>%1</filename>.", configFileName)));
0128         return reply;
0129     }
0130     if (!QFile::copy(originalConfigFileName, configFileName)) {
0131         reply = ActionReply::HelperErrorReply();
0132         reply.setError(3);
0133         reply.setErrorDescription(errorDescription(reply.errorCode(), i18n("Cannot copy original configuration file <filename>%1</filename> to <filename>%2</filename>.", originalConfigFileName, configFileName)));
0134         return reply;
0135     }
0136     return reply;
0137 }
0138 ActionReply Helper::install(QVariantMap args)
0139 {
0140     ActionReply reply;
0141     QString partition = args.value(QStringLiteral("partition")).toString();
0142     QString mountPoint = args.value(QStringLiteral("mountPoint")).toString();
0143     bool mbrInstall = args.value(QStringLiteral("mbrInstall")).toBool();
0144 
0145     if (mountPoint.isEmpty()) {
0146         for (int i = 0; QDir(mountPoint = QStringLiteral("%1/kcm-grub2-%2").arg(QDir::tempPath(), QString::number(i))).exists(); i++);
0147         if (!QDir().mkpath(mountPoint)) {
0148             reply = ActionReply::HelperErrorReply();
0149             reply.setError(4);
0150             reply.setErrorDescription(errorDescription(reply.errorCode(), i18n("Failed to create temporary mount point.")));
0151             return reply;
0152         }
0153         ActionReply mountReply = executeCommand(QStringList() << QStringLiteral("mount") << partition << mountPoint);
0154         if (mountReply.failed()) {
0155             return mountReply;
0156         }
0157     }
0158 
0159     QStringList grub_installCommand;
0160     grub_installCommand << grubInstallExePath() << QStringLiteral("--root-directory") << mountPoint;
0161     if (mbrInstall) {
0162         grub_installCommand << partition.remove(QRegExp(QLatin1String("\\d+")));
0163     } else {
0164         grub_installCommand << QStringLiteral("--force") << partition;
0165     }
0166     return executeCommand(grub_installCommand);
0167 }
0168 ActionReply Helper::load(QVariantMap args)
0169 {
0170     ActionReply reply;
0171     LoadOperations operations = (LoadOperations)(args.value(QStringLiteral("operations")).toInt());
0172 
0173     if (operations.testFlag(MenuFile)) {
0174         QFile file(grubMenuPath());
0175         bool ok = file.open(QIODevice::ReadOnly | QIODevice::Text);
0176         reply.addData(QStringLiteral("menuSuccess"), ok);
0177         if (ok) {
0178             reply.addData(QStringLiteral("menuContents"), file.readAll());
0179         } else {
0180             reply.addData(QStringLiteral("menuError"), file.error());
0181             reply.addData(QStringLiteral("menuErrorString"), file.errorString());
0182         }
0183     }
0184     if (operations.testFlag(ConfigurationFile)) {
0185         QFile file(grubConfigPath());
0186         bool ok = file.open(QIODevice::ReadOnly | QIODevice::Text);
0187         reply.addData(QStringLiteral("configSuccess"), ok);
0188         if (ok) {
0189             reply.addData(QStringLiteral("configContents"), file.readAll());
0190         } else {
0191             reply.addData(QStringLiteral("configError"), file.error());
0192             reply.addData(QStringLiteral("configErrorString"), file.errorString());
0193         }
0194     }
0195     if (operations.testFlag(EnvironmentFile)) {
0196         QFile file(grubEnvPath());
0197         bool ok = file.open(QIODevice::ReadOnly | QIODevice::Text);
0198         reply.addData(QStringLiteral("envSuccess"), ok);
0199         if (ok) {
0200             reply.addData(QStringLiteral("envContents"), file.readAll());
0201         } else {
0202             reply.addData(QStringLiteral("envError"), file.error());
0203             reply.addData(QStringLiteral("envErrorString"), file.errorString());
0204         }
0205     }
0206     if (operations.testFlag(MemtestFile)) {
0207         bool memtest = QFile::exists(grubMemtestPath());
0208         reply.addData(QStringLiteral("memtest"), memtest);
0209         if (memtest) {
0210             reply.addData(QStringLiteral("memtestOn"), (bool)(QFile::permissions(grubMemtestPath()) & (QFile::ExeOwner | QFile::ExeGroup | QFile::ExeOther)));
0211         }
0212     }
0213 #if HAVE_HD
0214     if (operations.testFlag(Vbe)) {
0215         QStringList gfxmodes;
0216         hd_data_t hd_data;
0217         memset(&hd_data, 0, sizeof(hd_data));
0218         hd_t *hd = hd_list(&hd_data, hw_framebuffer, 1, NULL);
0219         for (hd_res_t *res = hd->res; res; res = res->next) {
0220             if (res->any.type == res_framebuffer) {
0221                 gfxmodes += QString(QLatin1String("%1x%2x%3")).arg(QString::number(res->framebuffer.width), QString::number(res->framebuffer.height), QString::number(res->framebuffer.colorbits));
0222             }
0223         }
0224         hd_free_hd_list(hd);
0225         hd_free_hd_data(&hd_data);
0226         reply.addData(QLatin1String("gfxmodes"), gfxmodes);
0227     }
0228 #endif
0229     if (operations.testFlag(Locales)) {
0230         reply.addData(QStringLiteral("locales"), QDir(grubLocalePath()).entryList(QStringList() << QStringLiteral("*.mo"), QDir::Files).replaceInStrings(QRegExp(QLatin1String("\\.mo$")), QString()));
0231     }
0232     return reply;
0233 }
0234 ActionReply Helper::save(QVariantMap args)
0235 {
0236     ActionReply reply;
0237     QString configFileName = grubConfigPath();
0238     QByteArray rawConfigFileContents = args.value(QStringLiteral("rawConfigFileContents")).toByteArray();
0239     QByteArray rawDefaultEntry = args.value(QStringLiteral("rawDefaultEntry")).toByteArray();
0240     bool memtest = args.value(QStringLiteral("memtest")).toBool();
0241 
0242     QFile::copy(configFileName, configFileName + QLatin1String(".original"));
0243 
0244     QFile file(configFileName);
0245     if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
0246         reply = ActionReply::HelperErrorReply();
0247         reply.setError(5);
0248         reply.setErrorDescription(errorDescription(reply.errorCode(), file.errorString()));
0249         return reply;
0250     }
0251     file.write(rawConfigFileContents);
0252     file.close();
0253 
0254     if (args.contains(QStringLiteral("memtest"))) {
0255         QFile::Permissions permissions = QFile::permissions(grubMemtestPath());
0256         if (memtest) {
0257             permissions |= (QFile::ExeOwner | QFile::ExeUser | QFile::ExeGroup | QFile::ExeOther);
0258         } else {
0259             permissions &= ~(QFile::ExeOwner | QFile::ExeUser | QFile::ExeGroup | QFile::ExeOther);
0260         }
0261         QFile::setPermissions(grubMemtestPath(), permissions);
0262     }
0263 
0264     if (args.contains(QStringLiteral("LANG"))) {
0265         qputenv("LANG", args.value(QStringLiteral("LANG")).toByteArray());
0266     }
0267     ActionReply grub_mkconfigReply = executeCommand(QStringList() << grubMkconfigExePath() << QStringLiteral("-o") << grubMenuPath());
0268     if (grub_mkconfigReply.failed()) {
0269         return grub_mkconfigReply;
0270     }
0271     if (args.contains(QStringLiteral("LANGUAGE"))) {
0272         if (!setLang(args.value(QStringLiteral("LANGUAGE")).toString())) {
0273             qCritical() << "An error occurred while setting the language for the GRUB menu.";
0274             qCritical() << "The GRUB menu will not be properly translated!";
0275         }
0276     }
0277 
0278     ActionReply grub_set_defaultReply = executeCommand(QStringList() << grubSetDefaultExePath() << QString::fromUtf8(rawDefaultEntry.constData()));
0279     if (grub_set_defaultReply.failed()) {
0280         return grub_set_defaultReply;
0281     }
0282 
0283     return grub_mkconfigReply;
0284 }
0285 
0286 QString Helper::errorDescription(int errorCode, const QString &errorMessage) const
0287 {
0288     return i18nc("@info", "Error code: <numid>%1</numid><nl/>Error message: <message>%2</message>", errorCode, errorMessage);
0289 }
0290 
0291 KAUTH_HELPER_MAIN("org.kde.kcontrol.kcmgrub2", Helper)