helper.cpp 7.34 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/*
 *  tzone.cpp
 *
 *  Copyright (C) 1998 Luca Montecchiani <m.luca@usa.net>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 */

/*

 A helper that's run using KAuth and does the system modifications.

*/

#include "helper.h"

#include <config-workspace.h>

#include <sys/time.h>
#include <time.h>
#include <unistd.h>

#include <kcomponentdata.h>
#include <kconfig.h>
#include <kconfiggroup.h>
#include <kstandarddirs.h>
#include <kprocess.h>
#include <QFile>
#include <QDebug>

#if defined(USE_SOLARIS)
#include <ktemporaryfile.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#endif

// We cannot rely on the $PATH environment variable, because D-Bus activation
// clears it. So we have to use a reasonable default.
53
static const QString exePath = QStringLiteral("/usr/sbin:/usr/bin:/sbin:/bin");
54

55
int ClockHelper::ntp( const QStringList& ntpServers, bool ntpEnabled )
56
{
Lukáš Tinkl's avatar
Lukáš Tinkl committed
57
58
59
60
61
62
63
64
    int ret = 0;

    // write to the system config file
    QFile config_file(KDE_CONFDIR "/kcmclockrc");
    if(!config_file.exists()) {
        config_file.open(QIODevice::WriteOnly);
        config_file.close();
        config_file.setPermissions(QFile::ReadOwner | QFile::WriteOwner | QFile::ReadGroup | QFile::ReadOther);
65
    }
Lukáš Tinkl's avatar
Lukáš Tinkl committed
66
67
68
69
70
    KConfig _config(config_file.fileName(), KConfig::SimpleConfig);
    KConfigGroup config(&_config, "NTP");
    config.writeEntry("servers", ntpServers );
    config.writeEntry("enabled", ntpEnabled );

71
    QString ntpUtility = QStandardPaths::findExecutable(QStringLiteral("ntpdate"));
David Edmundson's avatar
David Edmundson committed
72
    if (ntpUtility.isEmpty()) {
73
        ntpUtility = QStandardPaths::findExecutable(QStringLiteral("rdate"));
74
75
    }

Lukáš Tinkl's avatar
Lukáš Tinkl committed
76
77
78
    if ( ntpEnabled && !ntpUtility.isEmpty() ) {
        // NTP Time setting
        QString timeServer = ntpServers.first();
79
        if( timeServer.indexOf( QRegExp(QStringLiteral(".*\\(.*\\)$")) ) != -1 ) {
Laurent Montel's avatar
Laurent Montel committed
80
81
            timeServer.remove( QRegExp(QStringLiteral(".*\\(")));
            timeServer.remove( QRegExp(QStringLiteral("\\).*")));
Lukáš Tinkl's avatar
Lukáš Tinkl committed
82
83
            // Would this be better?: s/^.*\(([^)]*)\).*$/\1/
        }
84

Lukáš Tinkl's avatar
Lukáš Tinkl committed
85
86
87
88
89
90
91
92
93
        KProcess proc;
        proc << ntpUtility << timeServer;
        if ( proc.execute() != 0 ) {
            ret |= NTPError;
        } else {
            toHwclock();
        }
    } else if( ntpEnabled ) {
        ret |= NTPError;
94
95
    }

Lukáš Tinkl's avatar
Lukáš Tinkl committed
96
    return ret;
97
98
99
100
101
102
}

int ClockHelper::date( const QString& newdate, const QString& olddate )
{
    struct timeval tv;

103
    tv.tv_sec = newdate.toULong() - olddate.toULong() + time(nullptr);
104
    tv.tv_usec = 0;
105
    if (settimeofday(&tv, nullptr)) {
106
107
108
109
110
111
112
113
114
115
116
        return DateError;
    }

    toHwclock();
    return 0;
}

// on non-Solaris systems which do not use /etc/timezone?
int ClockHelper::tz( const QString& selectedzone )
{
    int ret = 0;
117
118
119

    //only allow letters, numbers hyphen underscore plus and forward slash
    //allowed pattern taken from time-util.c in systemd
120
    if (!QRegExp(QStringLiteral("[a-zA-Z0-9-_+/]*")).exactMatch(selectedzone)) {
121
122
123
        return ret;
    }

Lukáš Tinkl's avatar
Lukáš Tinkl committed
124
    QString val;
125
#if defined(USE_SOLARIS)	// MARCO
Lukáš Tinkl's avatar
Lukáš Tinkl committed
126
127
128
129
    KTemporaryFile tf;
    tf.setPrefix("kde-tzone");
    tf.open();
    QTextStream ts(&tf);
130

Lukáš Tinkl's avatar
Lukáš Tinkl committed
131
132
    QFile fTimezoneFile(INITFILE);
    bool updatedFile = false;
133

Lukáš Tinkl's avatar
Lukáš Tinkl committed
134
135
136
    if (fTimezoneFile.open(QIODevice::ReadOnly))
    {
        bool found = false;
137

Lukáš Tinkl's avatar
Lukáš Tinkl committed
138
        QTextStream is(&fTimezoneFile);
139

Lukáš Tinkl's avatar
Lukáš Tinkl committed
140
141
142
143
        for (QString line = is.readLine(); !line.isNull();
             line = is.readLine())
        {
            if (line.find("TZ=") == 0)
144
            {
Lukáš Tinkl's avatar
Lukáš Tinkl committed
145
146
                ts << "TZ=" << selectedzone << endl;
                found = true;
147
            }
Lukáš Tinkl's avatar
Lukáš Tinkl committed
148
            else
149
            {
Lukáš Tinkl's avatar
Lukáš Tinkl committed
150
                ts << line << endl;
151
            }
Lukáš Tinkl's avatar
Lukáš Tinkl committed
152
        }
153

Lukáš Tinkl's avatar
Lukáš Tinkl committed
154
155
156
        if (!found)
        {
            ts << "TZ=" << selectedzone << endl;
157
158
        }

Lukáš Tinkl's avatar
Lukáš Tinkl committed
159
160
161
162
163
164
165
166
167
168
        updatedFile = true;
        fTimezoneFile.close();
    }

    if (updatedFile)
    {
        ts.device()->reset();
        fTimezoneFile.remove();

        if (fTimezoneFile.open(QIODevice::WriteOnly | QIODevice::Truncate))
169
        {
Lukáš Tinkl's avatar
Lukáš Tinkl committed
170
            QTextStream os(&fTimezoneFile);
171

Lukáš Tinkl's avatar
Lukáš Tinkl committed
172
173
            for (QString line = ts->readLine(); !line.isNull();
                 line = ts->readLine())
174
            {
Lukáš Tinkl's avatar
Lukáš Tinkl committed
175
                os << line << endl;
176
            }
Lukáš Tinkl's avatar
Lukáš Tinkl committed
177
178
179
180
181

            fchmod(fTimezoneFile.handle(),
                   S_IXUSR | S_IRUSR | S_IRGRP | S_IXGRP |
                   S_IROTH | S_IXOTH);
            fTimezoneFile.close();
182
        }
Lukáš Tinkl's avatar
Lukáš Tinkl committed
183
    }
184
185


Lukáš Tinkl's avatar
Lukáš Tinkl committed
186
    val = selectedzone;
187
#else
Lukáš Tinkl's avatar
Lukáš Tinkl committed
188
189
190
    QString tz = "/usr/share/zoneinfo/" + selectedzone;

    if (QFile::exists(tz)) { // make sure the new TZ really exists
191
        QFile::remove(QStringLiteral("/etc/localtime"));
Lukáš Tinkl's avatar
Lukáš Tinkl committed
192
193
194
    } else {
        return TimezoneError;
    }
195

196
    if (!QFile::link(tz, QStringLiteral("/etc/localtime"))) { // fail if we can't setup the new timezone
Lukáš Tinkl's avatar
Lukáš Tinkl committed
197
198
        return TimezoneError;
    }
199

200
    QFile fTimezoneFile(QStringLiteral("/etc/timezone"));
201

Lukáš Tinkl's avatar
Lukáš Tinkl committed
202
203
204
205
206
    if (fTimezoneFile.exists() && fTimezoneFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
        QTextStream t(&fTimezoneFile);
        t << selectedzone;
        fTimezoneFile.close();
    }
207
#endif // !USE_SOLARIS
Lukáš Tinkl's avatar
Lukáš Tinkl committed
208
    val = ':' + selectedzone;
209

Lukáš Tinkl's avatar
Lukáš Tinkl committed
210
211
    setenv("TZ", val.toLocal8Bit().constData(), 1);
    tzset();
212
213
214
215
216
217
218

    return ret;
}

int ClockHelper::tzreset()
{
#if !defined(USE_SOLARIS) // Do not update the System!
Lukáš Tinkl's avatar
Lukáš Tinkl committed
219
220
    unlink( "/etc/timezone" );
    unlink( "/etc/localtime" );
221

Lukáš Tinkl's avatar
Lukáš Tinkl committed
222
223
    setenv("TZ", "", 1);
    tzset();
224
225
226
227
228
229
#endif // !USE SOLARIS
    return 0;
}

void ClockHelper::toHwclock()
{
230
    QString hwclock = QStandardPaths::findExecutable(QStringLiteral("hwclock"), exePath.split(QLatin1Char(':')));
Lukáš Tinkl's avatar
Lukáš Tinkl committed
231
    if (!hwclock.isEmpty()) {
232
        KProcess::execute(hwclock, QStringList() << QStringLiteral("--systohc"));
Lukáš Tinkl's avatar
Lukáš Tinkl committed
233
    }
234
235
236
237
}

ActionReply ClockHelper::save(const QVariantMap &args)
{
238
239
240
241
    bool _ntp = args.value(QStringLiteral("ntp")).toBool();
    bool _date = args.value(QStringLiteral("date")).toBool();
    bool _tz = args.value(QStringLiteral("tz")).toBool();
    bool _tzreset = args.value(QStringLiteral("tzreset")).toBool();
Lukáš Tinkl's avatar
Lukáš Tinkl committed
242
243
244
245
246
247

    KComponentData data( "kcmdatetimehelper" );

    int ret = 0; // error code
    //  The order here is important
    if( _ntp )
248
        ret |= ntp( args.value(QStringLiteral("ntpServers")).toStringList(), args.value(QStringLiteral("ntpEnabled")).toBool());
Lukáš Tinkl's avatar
Lukáš Tinkl committed
249
    if( _date )
250
        ret |= date( args.value(QStringLiteral("newdate")).toString(), args.value(QStringLiteral("olddate")).toString() );
Lukáš Tinkl's avatar
Lukáš Tinkl committed
251
    if( _tz )
252
        ret |= tz( args.value(QStringLiteral("tzone")).toString() );
Lukáš Tinkl's avatar
Lukáš Tinkl committed
253
254
255
256
257
258
259
260
261
262
    if( _tzreset )
        ret |= tzreset();

    if (ret == 0) {
        return ActionReply::SuccessReply();
    } else {
        ActionReply reply(ActionReply::HelperErrorReply());
        reply.setErrorCode(static_cast<ActionReply::Error>(ret));
        return reply;
    }
263
264
265
}

KAUTH_HELPER_MAIN("org.kde.kcontrol.kcmclock", ClockHelper)