File indexing completed on 2024-05-12 05:35:39
0001 /* 0002 A helper that's run using KAuth and does the system modifications. 0003 SPDX-FileCopyrightText: 1998 Luca Montecchiani <m.luca@usa.net> 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 0006 */ 0007 0008 #include "helper.h" 0009 0010 #include <config-workspace.h> 0011 0012 #include <sys/time.h> 0013 #include <time.h> 0014 #include <unistd.h> 0015 0016 #include <KConfig> 0017 #include <KConfigGroup> 0018 #include <KProcess> 0019 #include <QDebug> 0020 #include <QFile> 0021 #include <QRegularExpression> 0022 0023 #if defined(USE_SOLARIS) 0024 #include <KTemporaryFile> 0025 #include <sys/param.h> 0026 #include <sys/stat.h> 0027 #include <sys/types.h> 0028 #endif 0029 0030 // We cannot rely on the $PATH environment variable, because D-Bus activation 0031 // clears it. So we have to use a reasonable default. 0032 static const QString exePath = QStringLiteral("/usr/sbin:/usr/bin:/sbin:/bin"); 0033 0034 int ClockHelper::ntp(const QStringList &ntpServers, bool ntpEnabled) 0035 { 0036 int ret = 0; 0037 0038 // write to the system config file 0039 QFile config_file(KDE_CONFDIR "/kcmclockrc"); 0040 if (!config_file.exists()) { 0041 config_file.open(QIODevice::WriteOnly); 0042 config_file.close(); 0043 config_file.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ReadGroup | QFile::ReadOther); 0044 } 0045 KConfig _config(config_file.fileName(), KConfig::SimpleConfig); 0046 KConfigGroup config(&_config, QStringLiteral("NTP")); 0047 config.writeEntry("servers", ntpServers); 0048 config.writeEntry("enabled", ntpEnabled); 0049 0050 QString ntpUtility = QStandardPaths::findExecutable(QStringLiteral("ntpdate")); 0051 if (ntpUtility.isEmpty()) { 0052 ntpUtility = QStandardPaths::findExecutable(QStringLiteral("rdate")); 0053 } 0054 0055 if (ntpEnabled && !ntpUtility.isEmpty()) { 0056 // NTP Time setting 0057 QString timeServer = ntpServers.first(); 0058 if (timeServer.indexOf(QRegularExpression(QStringLiteral(".*\\(.*\\)$"))) != -1) { 0059 timeServer.remove(QRegularExpression(QStringLiteral(".*\\("))); 0060 timeServer.remove(QRegularExpression(QStringLiteral("\\).*"))); 0061 // Would this be better?: s/^.*\(([^)]*)\).*$/\1/ 0062 } 0063 0064 KProcess proc; 0065 proc << ntpUtility << timeServer; 0066 if (proc.execute() != 0) { 0067 ret |= NTPError; 0068 } else { 0069 toHwclock(); 0070 } 0071 } else if (ntpEnabled) { 0072 ret |= NTPError; 0073 } 0074 0075 return ret; 0076 } 0077 0078 int ClockHelper::date(const QString &newdate, const QString &olddate) 0079 { 0080 struct timeval tv; 0081 0082 tv.tv_sec = newdate.toULong() - olddate.toULong() + time(nullptr); 0083 tv.tv_usec = 0; 0084 if (settimeofday(&tv, nullptr)) { 0085 return DateError; 0086 } 0087 0088 toHwclock(); 0089 return 0; 0090 } 0091 0092 // on non-Solaris systems which do not use /etc/timezone? 0093 int ClockHelper::tz(const QString &selectedzone) 0094 { 0095 int ret = 0; 0096 0097 // only allow letters, numbers hyphen underscore plus and forward slash 0098 // allowed pattern taken from time-util.c in systemd 0099 const auto rx = QRegularExpression(QRegularExpression::anchoredPattern(QStringLiteral("[a-zA-Z0-9-_+/]*"))); 0100 if (!rx.match(selectedzone).hasMatch()) { 0101 return ret; 0102 } 0103 0104 QString val; 0105 #if defined(USE_SOLARIS) // MARCO 0106 KTemporaryFile tf; 0107 tf.setPrefix("kde-tzone"); 0108 tf.open(); 0109 QTextStream ts(&tf); 0110 0111 QFile fTimezoneFile(INITFILE); 0112 bool updatedFile = false; 0113 0114 if (fTimezoneFile.open(QIODevice::ReadOnly)) { 0115 bool found = false; 0116 0117 QTextStream is(&fTimezoneFile); 0118 0119 for (QString line = is.readLine(); !line.isNull(); line = is.readLine()) { 0120 if (line.find("TZ=") == 0) { 0121 ts << "TZ=" << selectedzone << endl; 0122 found = true; 0123 } else { 0124 ts << line << endl; 0125 } 0126 } 0127 0128 if (!found) { 0129 ts << "TZ=" << selectedzone << endl; 0130 } 0131 0132 updatedFile = true; 0133 fTimezoneFile.close(); 0134 } 0135 0136 if (updatedFile) { 0137 ts.device()->reset(); 0138 fTimezoneFile.remove(); 0139 0140 if (fTimezoneFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { 0141 QTextStream os(&fTimezoneFile); 0142 0143 for (QString line = ts->readLine(); !line.isNull(); line = ts->readLine()) { 0144 os << line << endl; 0145 } 0146 0147 fchmod(fTimezoneFile.handle(), S_IXUSR | S_IRUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); 0148 fTimezoneFile.close(); 0149 } 0150 } 0151 0152 val = selectedzone; 0153 #else 0154 QString tz = "/usr/share/zoneinfo/" + selectedzone; 0155 0156 if (QFile::exists(tz)) { // make sure the new TZ really exists 0157 QFile::remove(QStringLiteral("/etc/localtime")); 0158 } else { 0159 return TimezoneError; 0160 } 0161 0162 if (!QFile::link(tz, QStringLiteral("/etc/localtime"))) { // fail if we can't setup the new timezone 0163 return TimezoneError; 0164 } 0165 0166 QFile fTimezoneFile(QStringLiteral("/etc/timezone")); 0167 0168 if (fTimezoneFile.exists() && fTimezoneFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { 0169 QTextStream t(&fTimezoneFile); 0170 t << selectedzone; 0171 fTimezoneFile.close(); 0172 } 0173 #endif // !USE_SOLARIS 0174 val = ':' + selectedzone; 0175 0176 setenv("TZ", val.toLocal8Bit().constData(), 1); 0177 tzset(); 0178 0179 return ret; 0180 } 0181 0182 int ClockHelper::tzreset() 0183 { 0184 #if !defined(USE_SOLARIS) // Do not update the System! 0185 unlink("/etc/timezone"); 0186 unlink("/etc/localtime"); 0187 0188 setenv("TZ", "", 1); 0189 tzset(); 0190 #endif // !USE SOLARIS 0191 return 0; 0192 } 0193 0194 void ClockHelper::toHwclock() 0195 { 0196 QString hwclock = QStandardPaths::findExecutable(QStringLiteral("hwclock"), exePath.split(QLatin1Char(':'))); 0197 if (!hwclock.isEmpty()) { 0198 KProcess::execute(hwclock, QStringList() << QStringLiteral("--systohc")); 0199 } 0200 } 0201 0202 ActionReply ClockHelper::save(const QVariantMap &args) 0203 { 0204 bool _ntp = args.value(QStringLiteral("ntp")).toBool(); 0205 bool _date = args.value(QStringLiteral("date")).toBool(); 0206 bool _tz = args.value(QStringLiteral("tz")).toBool(); 0207 bool _tzreset = args.value(QStringLiteral("tzreset")).toBool(); 0208 0209 int ret = 0; // error code 0210 // The order here is important 0211 if (_ntp) 0212 ret |= ntp(args.value(QStringLiteral("ntpServers")).toStringList(), args.value(QStringLiteral("ntpEnabled")).toBool()); 0213 if (_date) 0214 ret |= date(args.value(QStringLiteral("newdate")).toString(), args.value(QStringLiteral("olddate")).toString()); 0215 if (_tz) 0216 ret |= tz(args.value(QStringLiteral("tzone")).toString()); 0217 if (_tzreset) 0218 ret |= tzreset(); 0219 0220 if (ret == 0) { 0221 return ActionReply::SuccessReply(); 0222 } else { 0223 ActionReply reply(ActionReply::HelperErrorReply()); 0224 reply.setErrorCode(static_cast<ActionReply::Error>(ret)); 0225 return reply; 0226 } 0227 } 0228 0229 KAUTH_HELPER_MAIN("org.kde.kcontrol.kcmclock", ClockHelper)