File indexing completed on 2024-04-28 03:53:58
0001 /* 0002 SPDX-FileCopyrightText: 2020 Kai Uwe Broulik <kde@broulik.de> 0003 SPDX-FileCopyrightText: 2021 David Edmundson <davidedmundson@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "kupdatelaunchenvironmentjob.h" 0009 0010 #include <QDBusArgument> 0011 #include <QDBusConnection> 0012 #include <QDBusMetaType> 0013 #include <QDBusPendingReply> 0014 0015 #include <QTimer> 0016 0017 #include "kdbusaddons_debug.h" 0018 0019 class KUpdateLaunchEnvironmentJobPrivate 0020 { 0021 public: 0022 explicit KUpdateLaunchEnvironmentJobPrivate(KUpdateLaunchEnvironmentJob *q); 0023 void monitorReply(const QDBusPendingReply<> &reply); 0024 0025 static bool isPosixName(const QString &name); 0026 static bool isSystemdApprovedValue(const QString &value); 0027 0028 KUpdateLaunchEnvironmentJob *q; 0029 QProcessEnvironment environment; 0030 int pendingReplies = 0; 0031 }; 0032 0033 KUpdateLaunchEnvironmentJobPrivate::KUpdateLaunchEnvironmentJobPrivate(KUpdateLaunchEnvironmentJob *q) 0034 : q(q) 0035 { 0036 } 0037 0038 void KUpdateLaunchEnvironmentJobPrivate::monitorReply(const QDBusPendingReply<> &reply) 0039 { 0040 ++pendingReplies; 0041 0042 auto *watcher = new QDBusPendingCallWatcher(reply, q); 0043 QObject::connect(watcher, &QDBusPendingCallWatcher::finished, q, [this](QDBusPendingCallWatcher *watcher) { 0044 watcher->deleteLater(); 0045 --pendingReplies; 0046 0047 if (pendingReplies == 0) { 0048 Q_EMIT q->finished(); 0049 q->deleteLater(); 0050 } 0051 }); 0052 } 0053 0054 KUpdateLaunchEnvironmentJob::KUpdateLaunchEnvironmentJob(const QProcessEnvironment &environment) 0055 : d(new KUpdateLaunchEnvironmentJobPrivate(this)) 0056 { 0057 d->environment = environment; 0058 QTimer::singleShot(0, this, &KUpdateLaunchEnvironmentJob::start); 0059 } 0060 0061 KUpdateLaunchEnvironmentJob::~KUpdateLaunchEnvironmentJob() = default; 0062 0063 void KUpdateLaunchEnvironmentJob::start() 0064 { 0065 qDBusRegisterMetaType<QMap<QString, QString>>(); 0066 QMap<QString, QString> dbusActivationEnv; 0067 QStringList systemdUpdates; 0068 0069 for (const auto &varName : d->environment.keys()) { 0070 if (!KUpdateLaunchEnvironmentJobPrivate::isPosixName(varName)) { 0071 qCWarning(KDBUSADDONS_LOG) << "Skipping syncing of environment variable " << varName << "as name contains unsupported characters"; 0072 continue; 0073 } 0074 const QString value = d->environment.value(varName); 0075 0076 // plasma-session 0077 QDBusMessage plasmaSessionMsg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.Startup"), 0078 QStringLiteral("/Startup"), 0079 QStringLiteral("org.kde.Startup"), 0080 QStringLiteral("updateLaunchEnv")); 0081 plasmaSessionMsg.setArguments({QVariant::fromValue(varName), QVariant::fromValue(value)}); 0082 auto plasmaSessionReply = QDBusConnection::sessionBus().asyncCall(plasmaSessionMsg); 0083 d->monitorReply(plasmaSessionReply); 0084 0085 // DBus-activation environment 0086 dbusActivationEnv.insert(varName, value); 0087 0088 // _user_ systemd env 0089 // Systemd has stricter parsing of valid environment variables 0090 // https://github.com/systemd/systemd/issues/16704 0091 // validate here 0092 if (!KUpdateLaunchEnvironmentJobPrivate::isSystemdApprovedValue(value)) { 0093 qCWarning(KDBUSADDONS_LOG) << "Skipping syncing of environment variable " << varName << "as value contains unsupported characters"; 0094 continue; 0095 } 0096 const QString updateString = varName + QStringLiteral("=") + value; 0097 systemdUpdates.append(updateString); 0098 } 0099 0100 // DBus-activation environment 0101 QDBusMessage dbusActivationMsg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.DBus"), 0102 QStringLiteral("/org/freedesktop/DBus"), 0103 QStringLiteral("org.freedesktop.DBus"), 0104 QStringLiteral("UpdateActivationEnvironment")); 0105 dbusActivationMsg.setArguments({QVariant::fromValue(dbusActivationEnv)}); 0106 0107 auto dbusActivationReply = QDBusConnection::sessionBus().asyncCall(dbusActivationMsg); 0108 d->monitorReply(dbusActivationReply); 0109 0110 // _user_ systemd env 0111 QDBusMessage systemdActivationMsg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.systemd1"), 0112 QStringLiteral("/org/freedesktop/systemd1"), 0113 QStringLiteral("org.freedesktop.systemd1.Manager"), 0114 QStringLiteral("SetEnvironment")); 0115 systemdActivationMsg.setArguments({systemdUpdates}); 0116 0117 auto systemdActivationReply = QDBusConnection::sessionBus().asyncCall(systemdActivationMsg); 0118 d->monitorReply(systemdActivationReply); 0119 } 0120 0121 bool KUpdateLaunchEnvironmentJobPrivate::isPosixName(const QString &name) 0122 { 0123 // Posix says characters like % should be 'tolerated', but it gives issues in practice. 0124 // https://bugzilla.redhat.com/show_bug.cgi?id=1754395 0125 // https://bugzilla.redhat.com/show_bug.cgi?id=1879216 0126 // Ensure systemd compat by only allowing alphanumerics and _ in names. 0127 bool first = true; 0128 for (const QChar c : name) { 0129 if (first && !c.isLetter() && c != QLatin1Char('_')) { 0130 return false; 0131 } else if (first) { 0132 first = false; 0133 } else if (!c.isLetterOrNumber() && c != QLatin1Char('_')) { 0134 return false; 0135 } 0136 } 0137 return !first; 0138 } 0139 0140 bool KUpdateLaunchEnvironmentJobPrivate::isSystemdApprovedValue(const QString &value) 0141 { 0142 // systemd code checks that a value contains no control characters except \n \t 0143 // effectively copied from systemd's string_has_cc 0144 for (const char &it : value.toLatin1()) { 0145 if (it == QLatin1Char('\n') || it == QLatin1Char('\t')) { 0146 continue; 0147 } 0148 if (it > 0 && it < ' ') { 0149 return false; 0150 } 0151 if (it == 127) { 0152 return false; 0153 } 0154 } 0155 return true; 0156 } 0157 0158 #include "moc_kupdatelaunchenvironmentjob.cpp"