2020#include < QApplication>
2121#include < QDateTime>
2222#include < QDebug>
23+ #include < QDir>
2324#include < QFile>
25+ #include < QFileInfo>
2426#include < QLineEdit>
2527#include < QMessageBox>
2628#include < QProcess>
@@ -603,13 +605,65 @@ bool MXDateTime::validateServerList()
603605 return true ;
604606}
605607
608+ QString MXDateTime::systemdChronyUnit ()
609+ {
610+ const QStringList candidates {u" chronyd.service" _s, u" chrony.service" _s};
611+ for (const QString &unit : candidates) {
612+ QByteArray output;
613+ execute (u" systemctl" _s, {u" show" _s, u" -p" _s, u" LoadState" _s, u" --value" _s, unit}, &output, nullptr );
614+ if (QString::fromUtf8 (output).trimmed () == u" loaded" _s) {
615+ return unit;
616+ }
617+ }
618+ return {};
619+ }
620+
621+ QString MXDateTime::chronyConfigFile ()
622+ {
623+ if (QFile::exists (u" /etc/chrony/chrony.conf" _s)) {
624+ return u" /etc/chrony/chrony.conf" _s;
625+ }
626+ if (QFile::exists (u" /etc/chrony.conf" _s)) {
627+ return u" /etc/chrony.conf" _s;
628+ }
629+ return u" /etc/chrony/chrony.conf" _s;
630+ }
631+
632+ QString MXDateTime::chronySourcesFile ()
633+ {
634+ const QString configFile = chronyConfigFile ();
635+ QFile file (configFile);
636+ if (file.open (QFile::ReadOnly | QFile::Text)) {
637+ while (!file.atEnd ()) {
638+ QString line = QString::fromUtf8 (file.readLine ()).trimmed ();
639+ line = line.section (' #' , 0 , 0 ).trimmed ();
640+ if (line.startsWith (u" sourcedir " _s)) {
641+ const QStringList parts = line.split (' ' , Qt::SkipEmptyParts);
642+ if (parts.size () >= 2 ) {
643+ const QString dirPath = parts.at (1 );
644+ return QDir (dirPath).filePath (u" mx-datetime.sources" _s);
645+ }
646+ }
647+ }
648+ }
649+ if (QFileInfo (u" /etc/chrony/sources.d" _s).isDir ()) {
650+ return u" /etc/chrony/sources.d/mx-datetime.sources" _s;
651+ }
652+ if (QFileInfo (u" /etc/chrony.d" _s).isDir ()) {
653+ return u" /etc/chrony.d/mx-datetime.sources" _s;
654+ }
655+ return u" /etc/chrony/sources.d/mx-datetime.sources" _s;
656+ }
657+
606658void MXDateTime::loadNetworkTime ()
607659{
608660 while (tableServers->rowCount () > 0 ) {
609661 tableServers->removeRow (0 );
610662 }
611- loadSources (u" /etc/chrony/sources.d/mx-datetime.sources" _s);
612- const bool move = loadSources (u" /etc/chrony/chrony.conf" _s);
663+ const QString sourcesFile = chronySourcesFile ();
664+ const QString configFile = chronyConfigFile ();
665+ loadSources (sourcesFile);
666+ const bool move = loadSources (configFile);
613667
614668 // Stray change signals may have caused changedServers to be set.
615669 QApplication::processEvents ();
@@ -618,8 +672,15 @@ void MXDateTime::loadNetworkTime()
618672 if (sysInit != SystemD) {
619673 enabledNTP = shell (u" ls /etc/rc*.d | grep chrony | grep '^S'" _s);
620674 } else {
621- enabledNTP = shell (u" LANG=C systemctl status chrony | grep Loaded" _s
622- u" | grep service |cut -d';' -f2 |grep -q enabled" _s);
675+ const QString unit = systemdChronyUnit ();
676+ if (unit.isEmpty ()) {
677+ enabledNTP = false ;
678+ } else {
679+ QByteArray output;
680+ execute (u" systemctl" _s, {u" show" _s, u" -p" _s, u" UnitFileState" _s, u" --value" _s, unit}, &output, nullptr );
681+ const QString state = QString::fromUtf8 (output).trimmed ();
682+ enabledNTP = (state == u" enabled" _s || state == u" enabled-runtime" _s);
683+ }
623684 }
624685 checkAutoSync->setChecked (enabledNTP);
625686}
@@ -631,8 +692,11 @@ void MXDateTime::saveNetworkTime()
631692 const QString enable_disable (ntp ? u" enable" _s : u" disable" _s);
632693 const QString start_stop (ntp ? u" start" _s : u" stop" _s);
633694 if (sysInit == SystemD) {
634- executeAsRoot (u" systemctl" _s, {enable_disable, u" chrony" _s});
635- executeAsRoot (u" systemctl" _s, {start_stop, u" chrony" _s});
695+ const QString unit = systemdChronyUnit ();
696+ if (!unit.isEmpty ()) {
697+ executeAsRoot (u" systemctl" _s, {enable_disable, unit});
698+ executeAsRoot (u" systemctl" _s, {start_stop, unit});
699+ }
636700 } else if (sysInit == OpenRC) {
637701 if (QFile::exists (u" /etc/init.d/chronyd" _s)) {
638702 executeAsRoot (u" rc-update" _s, {ntp ? u" add" _s : u" del" _s, u" chronyd" _s});
@@ -646,6 +710,8 @@ void MXDateTime::saveNetworkTime()
646710
647711 // Generate chrony sources file.
648712 if (changedServers) {
713+ const QString sourcesFile = chronySourcesFile ();
714+ const QString configFile = chronyConfigFile ();
649715 QTemporaryFile file;
650716 if (file.open ()) {
651717 file.write (" # Generated by MX Date & Time - " );
@@ -668,16 +734,23 @@ void MXDateTime::saveNetworkTime()
668734 file.write (" \n " );
669735 }
670736 file.close ();
671- executeAsRoot (u" mv" _s, {file.fileName (), u" /etc/chrony/sources.d/mx-datetime.sources" _s});
672- executeAsRoot (u" chown" _s, {u" root:" _s, u" /etc/chrony/sources.d/mx-datetime.sources" _s});
673- executeAsRoot (u" chmod" _s, {u" +r" _s, u" /etc/chrony/sources.d/mx-datetime.sources" _s});
737+ const QDir sourcesDir = QFileInfo (sourcesFile).dir ();
738+ if (!sourcesDir.exists ()) {
739+ executeAsRoot (u" mkdir" _s, {u" -p" _s, sourcesDir.absolutePath ()});
740+ }
741+ executeAsRoot (u" mv" _s, {file.fileName (), sourcesFile});
742+ executeAsRoot (u" chown" _s, {u" root:" _s, sourcesFile});
743+ executeAsRoot (u" chmod" _s, {u" +r" _s, sourcesFile});
674744 }
675745 if (ntp) {
676- if (!clearSources (u" /etc/chrony/chrony.conf " _s )) {
746+ if (!clearSources (configFile )) {
677747 executeAsRoot (u" chronyc" _s, {u" reload" _s, u" sources" _s});
678748 } else {
679749 if (sysInit == SystemD) {
680- executeAsRoot (u" systemctl" _s, {u" restart" _s, u" chrony" _s});
750+ const QString unit = systemdChronyUnit ();
751+ if (!unit.isEmpty ()) {
752+ executeAsRoot (u" systemctl" _s, {u" restart" _s, unit});
753+ }
681754 } else {
682755 executeAsRoot (u" service" _s, {u" chrony" _s, u" restart" _s});
683756 }
@@ -692,6 +765,22 @@ bool MXDateTime::loadSources(const QString &filename)
692765 if (!file.open (QFile::ReadOnly | QFile::Text)) {
693766 return false ;
694767 }
768+ const auto isDuplicate = [this ](const QString &type, const QString &address, const QString &options) {
769+ const int serverCount = tableServers->rowCount ();
770+ for (int ixi = 0 ; ixi < serverCount; ++ixi) {
771+ auto *comboType = qobject_cast<QComboBox *>(tableServers->cellWidget (ixi, 0 ));
772+ if (!comboType) {
773+ continue ;
774+ }
775+ const QString rowType = comboType->currentData ().toString ();
776+ const QString rowAddress = tableServers->item (ixi, 1 )->text ().trimmed ();
777+ const QString rowOptions = tableServers->item (ixi, 2 )->text ().simplified ();
778+ if (rowType == type && rowAddress == address && rowOptions == options) {
779+ return true ;
780+ }
781+ }
782+ return false ;
783+ };
695784 bool hassrc = false ;
696785 while (!file.atEnd ()) {
697786 const QByteArray &bline = file.readLine ();
@@ -710,7 +799,11 @@ bool MXDateTime::loadSources(const QString &filename)
710799 options.append (' ' );
711800 options.append (args.at (ixi));
712801 }
713- addServerRow (enabled, curarg, args.at (1 ), options.trimmed ());
802+ const QString address = args.at (1 );
803+ const QString trimmedOptions = options.trimmed ();
804+ if (!isDuplicate (curarg, address, trimmedOptions)) {
805+ addServerRow (enabled, curarg, address, trimmedOptions);
806+ }
714807 hassrc = true ;
715808 }
716809 }
@@ -730,8 +823,8 @@ bool MXDateTime::clearSources(const QString &filename)
730823 }
731824 while (!file.atEnd ()) {
732825 const QByteArray &bline = file.readLine ();
733- static const QRegularExpression tregex (u" ^\\ s? (pool|server|peer)\\ s" _s);
734- if (QString (bline).contains (tregex)) {
826+ static const QRegularExpression tregex (u" ^\\ s* (pool|server|peer)\\ s" _s);
827+ if (QString::fromUtf8 (bline).contains (tregex)) {
735828 confdata.append (" ##" );
736829 changed = true ;
737830 }
0 commit comments