Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package kcron for openSUSE:Factory checked in at 2022-03-01 17:03:14 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/kcron (Old) and /work/SRC/openSUSE:Factory/.kcron.new.1958 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "kcron" Tue Mar 1 17:03:14 2022 rev:106 rq:957602 version:21.12.2 Changes: -------- --- /work/SRC/openSUSE:Factory/kcron/kcron.changes 2021-12-13 20:47:48.684535698 +0100 +++ /work/SRC/openSUSE:Factory/.kcron.new.1958/kcron.changes 2022-03-01 17:03:34.828333640 +0100 @@ -1,0 +2,33 @@ +Fri Feb 25 12:40:01 UTC 2022 - Christophe Giboudeaux <[email protected]> + +- Add more upstream changes (boo#1195154, boo#1193945): + * 0001-Write-into-crontab-instead-of-replacing-the-file.patch + +------------------------------------------------------------------- +Thu Feb 17 08:40:15 UTC 2022 - Christophe Giboudeaux <[email protected]> + +- Add upstream change: + * 0001-Improve-temporary-file-handling.patch (boo#1195154, boo#1195152, CVE-2022-24986) + +------------------------------------------------------------------- +Tue Feb 1 13:02:46 UTC 2022 - Christophe Giboudeaux <[email protected]> + +- Update to 21.12.2 + * New bugfix release + * For more details please see: + * https://kde.org/announcements/gear/21.12.2/ +- No code change since 21.12.1 +- Add upstream patch: + 0001-KCronHelper-Return-error-when-things-don-t-work-out.patch + (contributes to boo#1195154, boo#1193945) + +------------------------------------------------------------------- +Tue Jan 4 10:25:25 UTC 2022 - Christophe Giboudeaux <[email protected]> + +- Update to 21.12.1 + * New bugfix release + * For more details please see: + * https://kde.org/announcements/gear/21.12.1/ +- No code change since 21.12.0 + +------------------------------------------------------------------- Old: ---- kcron-21.12.0.tar.xz kcron-21.12.0.tar.xz.sig New: ---- 0001-Improve-temporary-file-handling.patch 0001-KCronHelper-Return-error-when-things-don-t-work-out.patch 0001-Write-into-crontab-instead-of-replacing-the-file.patch kcron-21.12.2.tar.xz kcron-21.12.2.tar.xz.sig ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ kcron.spec ++++++ --- /var/tmp/diff_new_pack.kwKPDD/_old 2022-03-01 17:03:35.572333837 +0100 +++ /var/tmp/diff_new_pack.kwKPDD/_new 2022-03-01 17:03:35.576333838 +0100 @@ -18,19 +18,25 @@ # Latest stable Applications (e.g. 17.08 in KA, but 17.11.80 in KUA) %{!?_kapp_version: %define _kapp_version %(echo %{version}| awk -F. '{print $1"."$2}')} -%bcond_without lang +%bcond_without released Name: kcron -Version: 21.12.0 +Version: 21.12.2 Release: 0 Summary: Cron job configuration tool License: GPL-2.0-or-later Group: System/Management URL: https://www.kde.org Source: https://download.kde.org/stable/release-service/%{version}/src/%{name}-%{version}.tar.xz -%if %{with lang} +%if %{with released} Source1: https://download.kde.org/stable/release-service/%{version}/src/%{name}-%{version}.tar.xz.sig Source2: applications.keyring %endif +# PATCH-FIX-UPSTREAM +Patch0: 0001-KCronHelper-Return-error-when-things-don-t-work-out.patch +# PATCH-FIX-UPSTREAM +Patch1: 0001-Improve-temporary-file-handling.patch +# PATCH-FIX-UPSTREAM +Patch2: 0001-Write-into-crontab-instead-of-replacing-the-file.patch BuildRequires: extra-cmake-modules BuildRequires: cmake(KF5DocTools) BuildRequires: cmake(KF5I18n) @@ -56,7 +62,7 @@ %install %kf5_makeinstall -C build - %if %{with lang} + %if %{with released} %find_lang %{name} --with-man --all-name %{kf5_find_htmldocs} %endif @@ -78,7 +84,7 @@ %{_kf5_sharedir}/dbus-1/system.d/local.kcron.crontab.conf %{_kf5_sharedir}/polkit-1/actions/local.kcron.crontab.policy -%if %{with lang} +%if %{with released} %files lang -f %{name}.lang %endif ++++++ 0001-Improve-temporary-file-handling.patch ++++++ >From ef4266e3d5ea741c4d4f442a2cb12a317d7502a1 Mon Sep 17 00:00:00 2001 From: Albert Astals Cid <[email protected]> Date: Tue, 15 Feb 2022 23:32:22 +0100 Subject: [PATCH] Improve temporary file handling --- src/crontablib/ctSystemCron.cpp | 21 ++------- src/crontablib/ctcron.cpp | 79 +++++++++++++-------------------- src/crontablib/ctcron.h | 12 ++--- src/helper/kcronhelper.cpp | 5 ++- 4 files changed, 42 insertions(+), 75 deletions(-) diff --git a/src/crontablib/ctSystemCron.cpp b/src/crontablib/ctSystemCron.cpp index 4b17042..e7fc535 100644 --- a/src/crontablib/ctSystemCron.cpp +++ b/src/crontablib/ctSystemCron.cpp @@ -11,7 +11,7 @@ #include <KLocalizedString> #include <KShell> -#include <QTemporaryFile> +#include <QFileInfo> #include "cthost.h" #include "cttask.h" @@ -28,20 +28,6 @@ CTSystemCron::CTSystemCron(const QString &crontabBinary) d->crontabBinary = crontabBinary; - QTemporaryFile tmp; - tmp.open(); - d->tmpFileName = tmp.fileName(); - - CommandLine readCommandLine; - - readCommandLine.commandLine = QStringLiteral("cat"); - readCommandLine.parameters << QStringLiteral("/etc/crontab"); - readCommandLine.standardOutputFile = d->tmpFileName; - - d->writeCommandLine.commandLine = QStringLiteral("cat"); - d->writeCommandLine.parameters << d->tmpFileName; - d->writeCommandLine.standardOutputFile = QStringLiteral("/etc/crontab"); - d->userLogin = i18n("root"); d->userRealName = d->userLogin; @@ -50,8 +36,9 @@ CTSystemCron::CTSystemCron(const QString &crontabBinary) // Don't set error if it can't be read, it means the user // doesn't have a crontab. - if (readCommandLine.execute().exitCode == 0) { - this->parseFile(d->tmpFileName); + const QString crontabFile = QStringLiteral("/etc/crontab"); + if (QFileInfo::exists(crontabFile)) { + parseFile(crontabFile); } d->initialTaskCount = d->task.size(); diff --git a/src/crontablib/ctcron.cpp b/src/crontablib/ctcron.cpp index 9d4ef26..b6bd90b 100644 --- a/src/crontablib/ctcron.cpp +++ b/src/crontablib/ctcron.cpp @@ -35,10 +35,6 @@ CommandLineStatus CommandLine::execute() { QProcess process; - if (!standardOutputFile.isEmpty()) { - process.setStandardOutputFile(standardOutputFile); - } - int exitCode; process.start(commandLine, parameters); if (!process.waitForStarted()) { @@ -51,9 +47,6 @@ CommandLineStatus CommandLine::execute() CommandLineStatus commandLineStatus; commandLineStatus.commandLine = commandLine + QLatin1String(" ") + parameters.join(QLatin1String(" ")); - if (!standardOutputFile.isEmpty()) { - commandLineStatus.commandLine += QLatin1String(" > ") + standardOutputFile; - } commandLineStatus.standardOutput = QLatin1String(process.readAllStandardOutput()); commandLineStatus.standardError = QLatin1String(process.readAllStandardError()); @@ -73,27 +66,15 @@ CTCron::CTCron(const QString &crontabBinary, const struct passwd *userInfos, boo d->crontabBinary = crontabBinary; - QTemporaryFile tmp; - tmp.open(); - d->tmpFileName = tmp.fileName(); - CommandLine readCommandLine; // regular user, so provide user's own crontab if (currentUserCron) { readCommandLine.commandLine = d->crontabBinary; readCommandLine.parameters << QStringLiteral("-l"); - readCommandLine.standardOutputFile = d->tmpFileName; - - d->writeCommandLine.commandLine = d->crontabBinary; - d->writeCommandLine.parameters << d->tmpFileName; } else { readCommandLine.commandLine = d->crontabBinary; readCommandLine.parameters << QStringLiteral("-u") << QLatin1String(userInfos->pw_name) << QStringLiteral("-l"); - readCommandLine.standardOutputFile = d->tmpFileName; - - d->writeCommandLine.commandLine = d->crontabBinary; - d->writeCommandLine.parameters << QStringLiteral("-u") << QLatin1String(userInfos->pw_name) << d->tmpFileName; } d->initialTaskCount = 0; @@ -108,7 +89,8 @@ CTCron::CTCron(const QString &crontabBinary, const struct passwd *userInfos, boo // Don't set error if it can't be read, it means the user doesn't have a crontab. CommandLineStatus commandLineStatus = readCommandLine.execute(); if (commandLineStatus.exitCode == 0) { - this->parseFile(d->tmpFileName); + QTextStream stream(&commandLineStatus.standardOutput); + parseTextStream(&stream); } else { qCDebug(KCM_CRON_LOG) << "Error when executing command" << commandLineStatus.commandLine; qCDebug(KCM_CRON_LOG) << "Standard output :" << commandLineStatus.standardOutput; @@ -171,12 +153,17 @@ void CTCron::parseFile(const QString &fileName) return; } + QTextStream in(&file); + parseTextStream(&in); +} + +void CTCron::parseTextStream(QTextStream *stream) +{ QString comment; bool leadingComment = true; - QTextStream in(&file); - while (!in.atEnd()) { - QString line = in.readLine(); + while (!stream->atEnd()) { + QString line = stream->readLine(); // search for comments "#" but not disabled tasks "#\" if (line.indexOf(QLatin1String("#")) == 0 && line.indexOf(QLatin1String("\\")) != 1) { @@ -257,24 +244,6 @@ CTCron::~CTCron() delete d; } -bool CTCron::saveToFile(const QString &fileName) -{ - QFile file(fileName); - if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { - return false; - } - - // qCDebug(KCM_CRON_LOG) << exportCron(); - - QTextStream out(&file); - out << exportCron(); - - out.flush(); - file.close(); - - return true; -} - CTSaveStatus CTCron::prepareSaveStatusError(const CommandLineStatus &commandLineStatus) { QString standardOutput; @@ -307,24 +276,29 @@ CTSaveStatus CTCron::prepareSaveStatusError(const CommandLineStatus &commandLine CTSaveStatus CTCron::save() { // write to temp file - bool saveStatus = saveToFile(d->tmpFileName); - if (!saveStatus) { - return CTSaveStatus(i18n("Unable to open crontab file for writing"), i18n("The file %1 could not be opened.", d->tmpFileName)); + QTemporaryFile tmp; + if (!tmp.open()) { + return CTSaveStatus(i18n("Unable to open crontab file for writing"), i18n("The file %1 could not be opened.", tmp.fileName())); + } + + { + QTextStream out(&tmp); + out << exportCron(); + out.flush(); } + tmp.close(); // For root permissions. if (d->systemCron) { qCDebug(KCM_CRON_LOG) << "Attempting to save system cron"; QVariantMap args; - args.insert(QStringLiteral("source"), d->tmpFileName); - args.insert(QStringLiteral("target"), d->writeCommandLine.standardOutputFile); + args.insert(QStringLiteral("source"), tmp.fileName()); KAuth::Action saveAction(QStringLiteral("local.kcron.crontab.save")); saveAction.setHelperId(QStringLiteral("local.kcron.crontab")); saveAction.setArguments(args); KAuth::ExecuteJob *job = saveAction.execute(); if (!job->exec()) qCDebug(KCM_CRON_LOG) << "KAuth returned an error: " << job->error() << job->errorText(); - QFile::remove(d->tmpFileName); if (job->error() > 0) { return CTSaveStatus(i18n("KAuth::ExecuteJob Error"), job->errorText()); } @@ -333,8 +307,15 @@ CTSaveStatus CTCron::save() else { qCDebug(KCM_CRON_LOG) << "Attempting to save user cron"; // Save without root permissions. - const CommandLineStatus commandLineStatus = d->writeCommandLine.execute(); - QFile::remove(d->tmpFileName); + CommandLine writeCommandLine; + writeCommandLine.commandLine = d->crontabBinary; + if (d->currentUserCron) { + writeCommandLine.parameters << tmp.fileName(); + } else { + writeCommandLine.parameters << QStringLiteral("-u") << d->userLogin << tmp.fileName(); + } + + const CommandLineStatus commandLineStatus = writeCommandLine.execute(); if (commandLineStatus.exitCode != 0) { return prepareSaveStatusError(commandLineStatus); } diff --git a/src/crontablib/ctcron.h b/src/crontablib/ctcron.h index b8d4ac0..f02b136 100644 --- a/src/crontablib/ctcron.h +++ b/src/crontablib/ctcron.h @@ -16,6 +16,9 @@ class CTTask; class CTVariable; class CTInitializationError; +class QFile; +class QTextStream; + struct passwd; #include "ctSaveStatus.h" @@ -38,8 +41,6 @@ public: QStringList parameters; - QString standardOutputFile; - CommandLineStatus execute(); }; @@ -86,10 +87,6 @@ public: int initialTaskCount; int initialVariableCount; - CommandLine writeCommandLine; - - QString tmpFileName; - /** * Contains path to the crontab binary file. */ @@ -201,8 +198,7 @@ protected: * Parses crontab file format. */ void parseFile(const QString &fileName); - - bool saveToFile(const QString &fileName); + void parseTextStream(QTextStream *stream); CTSaveStatus prepareSaveStatusError(const CommandLineStatus &commandLineStatus); // d probably stands for data. diff --git a/src/helper/kcronhelper.cpp b/src/helper/kcronhelper.cpp index d610c00..96fe8a0 100644 --- a/src/helper/kcronhelper.cpp +++ b/src/helper/kcronhelper.cpp @@ -32,7 +32,7 @@ ActionReply KcronHelper::save(const QVariantMap &args) { qCDebug(KCM_CRON_HELPER_LOG) << "running actions"; const QString source = args[QLatin1String("source")].toString(); - const QString destination = args[QLatin1String("target")].toString(); + const QString destination = QStringLiteral("/etc/crontab"); { QFile destinationFile(destination); if (destinationFile.exists() && !destinationFile.remove()) { @@ -44,6 +44,9 @@ ActionReply KcronHelper::save(const QVariantMap &args) } { QFile sourceFile(source); + if (!sourceFile.setPermissions(QFileDevice::ReadOwner | QFileDevice::WriteOwner | QFileDevice::ReadGroup | QFileDevice::ReadOther)) { + qCWarning(KCM_CRON_HELPER_LOG) << "can't change permissions to 644"; + } if (!sourceFile.copy(destination)) { qCWarning(KCM_CRON_HELPER_LOG) << "can't write into the system file" << sourceFile.errorString(); ActionReply reply = ActionReply::HelperErrorReply(); -- 2.35.1 ++++++ 0001-KCronHelper-Return-error-when-things-don-t-work-out.patch ++++++ >From 2c04c9f665283e8480a65f4ac0accfe6a8e0539a Mon Sep 17 00:00:00 2001 From: Albert Astals Cid <[email protected]> Date: Mon, 31 Jan 2022 23:45:12 +0100 Subject: [PATCH] KCronHelper: Return error when things don't work out --- src/helper/kcronhelper.cpp | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/helper/kcronhelper.cpp b/src/helper/kcronhelper.cpp index c5d3df2..d610c00 100644 --- a/src/helper/kcronhelper.cpp +++ b/src/helper/kcronhelper.cpp @@ -33,11 +33,23 @@ ActionReply KcronHelper::save(const QVariantMap &args) qCDebug(KCM_CRON_HELPER_LOG) << "running actions"; const QString source = args[QLatin1String("source")].toString(); const QString destination = args[QLatin1String("target")].toString(); - if (!QFile::remove(destination)) { - qCWarning(KCM_CRON_HELPER_LOG) << "can't remove file, it doesn't exist"; + { + QFile destinationFile(destination); + if (destinationFile.exists() && !destinationFile.remove()) { + ActionReply reply = ActionReply::HelperErrorReply(); + qCWarning(KCM_CRON_HELPER_LOG) << "can't remove file" << destinationFile.errorString(); + reply.setErrorDescription(destinationFile.errorString()); + return reply; + } } - if (!QFile::copy(source, destination)) { - qCWarning(KCM_CRON_HELPER_LOG) << "can't write into the system file, something went wrong"; + { + QFile sourceFile(source); + if (!sourceFile.copy(destination)) { + qCWarning(KCM_CRON_HELPER_LOG) << "can't write into the system file" << sourceFile.errorString(); + ActionReply reply = ActionReply::HelperErrorReply(); + reply.setErrorDescription(sourceFile.errorString()); + return reply; + } } return ActionReply::SuccessReply(); } -- 2.34.1 ++++++ 0001-Write-into-crontab-instead-of-replacing-the-file.patch ++++++ >From db2185dc9d9a423b0375880522ec85acd6decd1c Mon Sep 17 00:00:00 2001 From: Albert Astals Cid <[email protected]> Date: Wed, 23 Feb 2022 23:42:06 +0100 Subject: [PATCH] Write into crontab instead of replacing the file Keeps permissions, owners, etc. --- src/helper/kcronhelper.cpp | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/src/helper/kcronhelper.cpp b/src/helper/kcronhelper.cpp index 96fe8a0..eafb4ac 100644 --- a/src/helper/kcronhelper.cpp +++ b/src/helper/kcronhelper.cpp @@ -31,29 +31,38 @@ ActionReply KcronHelper::save(const QVariantMap &args) { qCDebug(KCM_CRON_HELPER_LOG) << "running actions"; - const QString source = args[QLatin1String("source")].toString(); - const QString destination = QStringLiteral("/etc/crontab"); + + QByteArray newCronData; { - QFile destinationFile(destination); - if (destinationFile.exists() && !destinationFile.remove()) { + const QString source = args[QLatin1String("source")].toString(); + QFile sourceFile(source); + if (!sourceFile.open(QIODevice::ReadOnly)) { + qCWarning(KCM_CRON_HELPER_LOG) << "can't open source file for reading" << source << sourceFile.errorString(); ActionReply reply = ActionReply::HelperErrorReply(); - qCWarning(KCM_CRON_HELPER_LOG) << "can't remove file" << destinationFile.errorString(); - reply.setErrorDescription(destinationFile.errorString()); + reply.setErrorDescription(sourceFile.errorString()); return reply; } + + newCronData = sourceFile.readAll(); } + { - QFile sourceFile(source); - if (!sourceFile.setPermissions(QFileDevice::ReadOwner | QFileDevice::WriteOwner | QFileDevice::ReadGroup | QFileDevice::ReadOther)) { - qCWarning(KCM_CRON_HELPER_LOG) << "can't change permissions to 644"; - } - if (!sourceFile.copy(destination)) { - qCWarning(KCM_CRON_HELPER_LOG) << "can't write into the system file" << sourceFile.errorString(); + const QString destination = QStringLiteral("/etc/crontab"); + QFile destinationFile(destination); + if (!destinationFile.open(QIODevice::WriteOnly)) { ActionReply reply = ActionReply::HelperErrorReply(); - reply.setErrorDescription(sourceFile.errorString()); + qCWarning(KCM_CRON_HELPER_LOG) << "can't open destination file for writing" << destinationFile.errorString(); + reply.setErrorDescription(destinationFile.errorString()); return reply; } + + if (destinationFile.write(newCronData) < 0) { + ActionReply reply = ActionReply::HelperErrorReply(); + qCWarning(KCM_CRON_HELPER_LOG) << "writing to destination file failed" << destinationFile.errorString(); + reply.setErrorDescription(destinationFile.errorString()); + } } + return ActionReply::SuccessReply(); } -- 2.35.1 ++++++ kcron-21.12.0.tar.xz -> kcron-21.12.2.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kcron-21.12.0/po/ca/kcron.po new/kcron-21.12.2/po/ca/kcron.po --- old/kcron-21.12.0/po/ca/kcron.po 2021-12-03 01:14:33.000000000 +0100 +++ new/kcron-21.12.2/po/ca/kcron.po 2022-02-01 01:12:05.000000000 +0100 @@ -1019,16 +1019,3 @@ #, kde-format msgid "Delete the selected variable." msgstr "Suprimeix la variable seleccionada." - -#~ msgid "All users" -#~ msgstr "Tots els usuaris" - -#~ msgctxt "User login: errorMessage" -#~ msgid "User %1: %2" -#~ msgstr "Usuari %1: %2" - -#~ msgid "Cron of User:" -#~ msgstr "Cron de l'usuari:" - -#~ msgid "Show All Personal Crons" -#~ msgstr "Mostra tots els Cron personals" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kcron-21.12.0/po/ca@valencia/kcron.po new/kcron-21.12.2/po/ca@valencia/kcron.po --- old/kcron-21.12.0/po/ca@valencia/kcron.po 2021-12-03 01:14:33.000000000 +0100 +++ new/kcron-21.12.2/po/ca@valencia/kcron.po 2022-02-01 01:12:05.000000000 +0100 @@ -1018,16 +1018,3 @@ #, kde-format msgid "Delete the selected variable." msgstr "Suprimeix la variable seleccionada." - -#~ msgid "All users" -#~ msgstr "Tots els usuaris" - -#~ msgctxt "User login: errorMessage" -#~ msgid "User %1: %2" -#~ msgstr "Usuari %1: %2" - -#~ msgid "Cron of User:" -#~ msgstr "Cron de l'usuari:" - -#~ msgid "Show All Personal Crons" -#~ msgstr "Mostra tots els Cron personals" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kcron-21.12.0/po/zh_CN/kcron.po new/kcron-21.12.2/po/zh_CN/kcron.po --- old/kcron-21.12.0/po/zh_CN/kcron.po 2021-12-03 01:14:33.000000000 +0100 +++ new/kcron-21.12.2/po/zh_CN/kcron.po 2022-02-01 01:12:05.000000000 +0100 @@ -9,7 +9,7 @@ "Project-Id-Version: kdeorg\n" "Report-Msgid-Bugs-To: https://bugs.kde.org\n" "POT-Creation-Date: 2021-10-08 00:19+0000\n" -"PO-Revision-Date: 2021-11-30 15:24\n" +"PO-Revision-Date: 2022-01-08 15:25\n" "Last-Translator: \n" "Language-Team: Chinese Simplified\n" "Language: zh_CN\n" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/kcron-21.12.0/src/kcm_cron.desktop new/kcron-21.12.2/src/kcm_cron.desktop --- old/kcron-21.12.0/src/kcm_cron.desktop 2021-11-30 03:53:39.000000000 +0100 +++ new/kcron-21.12.2/src/kcm_cron.desktop 2021-12-06 03:46:29.000000000 +0100 @@ -141,7 +141,7 @@ X-KDE-Keywords[ca@valencia]=cron,crontab,planificat,tasques,tasca,planificaci??,vixie X-KDE-Keywords[cs]=cron,crontab,napl??nov??no,??lohy,??loha,napl??novat,vixie X-KDE-Keywords[da]=cron,crontab,skemalagt,opgaver,opgaveskema,skema,vixie -X-KDE-Keywords[de]=cron,crontab,scheduled,tasks,task,schedule,vixie,aufgaben,planung +X-KDE-Keywords[de]=aufgaben,planung X-KDE-Keywords[el]=cron,crontab,??????????????????????????????????,????????????????,??????????????,??????????????????????????????,vixie X-KDE-Keywords[en_GB]=cron,crontab,scheduled,tasks,task,schedule,vixie X-KDE-Keywords[es]=cron,crontab,programado,tareas,tarea,programar,vixie
