summarylogtreecommitdiffstats
diff options
context:
space:
mode:
authorKonstantin Stepanov2015-06-19 11:04:47 +0300
committerKonstantin Stepanov2015-06-19 11:04:47 +0300
commite3857f34c0bb79f2276ab2df7b748f0f6e1a631e (patch)
treec41743f98b55eca9a67443be23f27f44e8b91a6e
downloadaur-e3857f34c0bb79f2276ab2df7b748f0f6e1a631e.tar.gz
import package from old aur
-rw-r--r--.SRCINFO30
-rw-r--r--PKGBUILD42
-rw-r--r--cron.target5
-rwxr-xr-xcrontab95
-rw-r--r--crontab.132
-rw-r--r--crontab.5377
-rwxr-xr-xsystemd-crontab-generator301
-rw-r--r--systemd-crontab-generator.153
-rwxr-xr-xsystemd-crontab-update4
9 files changed, 939 insertions, 0 deletions
diff --git a/.SRCINFO b/.SRCINFO
new file mode 100644
index 000000000000..d0e35c0306d2
--- /dev/null
+++ b/.SRCINFO
@@ -0,0 +1,30 @@
+pkgbase = systemd-crontab-generator
+ pkgdesc = systemd generator to generate timers/services from crontab and anacrontab files
+ pkgver = 0.8
+ pkgrel = 1
+ url = https://github.com/kstep/systemd-crontab-generator
+ arch = any
+ license = GPL3
+ depends = python2
+ depends = systemd
+ provides = cron
+ provides = anacron
+ replaces = cron
+ replaces = anacron
+ source = systemd-crontab-generator
+ source = systemd-crontab-update
+ source = cron.target
+ source = crontab
+ source = systemd-crontab-generator.1
+ source = crontab.1
+ source = crontab.5
+ md5sums = 3e6e2fb5bf3a6f05cacfbbb113516026
+ md5sums = 6f00710ad710e319b52edef3e98bd010
+ md5sums = 97450f27b69a1e88f1b21faad403df7c
+ md5sums = 4ac2cfc8de6dabf2e08f39b3c3557879
+ md5sums = 15acf6fd2a9533c13ce21c6e03210194
+ md5sums = d863925d682395cef72701725f180884
+ md5sums = f5e92c03bcb37acd580e2e27f5facc6a
+
+pkgname = systemd-crontab-generator
+
diff --git a/PKGBUILD b/PKGBUILD
new file mode 100644
index 000000000000..3c2b3f4bfaee
--- /dev/null
+++ b/PKGBUILD
@@ -0,0 +1,42 @@
+# Maintainer: Konstantin Stepanov <me@kstep.me>
+pkgname=systemd-crontab-generator
+pkgver=0.8
+pkgrel=1
+pkgdesc="systemd generator to generate timers/services from crontab and anacrontab files"
+url="https://github.com/kstep/systemd-crontab-generator"
+arch=('any')
+license=('GPL3')
+depends=('python2' 'systemd')
+provides=('cron' 'anacron')
+replaces=('cron' 'anacron')
+source=(systemd-crontab-generator
+ systemd-crontab-update
+ cron.target
+ crontab
+ systemd-crontab-generator.1
+ crontab.1
+ crontab.5)
+md5sums=('3e6e2fb5bf3a6f05cacfbbb113516026'
+ '6f00710ad710e319b52edef3e98bd010'
+ '97450f27b69a1e88f1b21faad403df7c'
+ '4ac2cfc8de6dabf2e08f39b3c3557879'
+ '15acf6fd2a9533c13ce21c6e03210194'
+ 'd863925d682395cef72701725f180884'
+ 'f5e92c03bcb37acd580e2e27f5facc6a')
+
+build() {
+ echo
+}
+
+package() {
+ install --mode=0755 -D systemd-crontab-generator ${pkgdir}/usr/lib/systemd/system-generators/systemd-crontab-generator
+ install --mode=0644 -D systemd-crontab-generator.1 ${pkgdir}/usr/share/man/man1/systemd-crontab-generator.1
+ gzip ${pkgdir}/usr/share/man/man1/systemd-crontab-generator.1
+ install --mode=0755 -D systemd-crontab-update ${pkgdir}/usr/bin/systemd-crontab-update
+ install --mode=0644 -D cron.target ${pkgdir}/usr/lib/systemd/system/cron.target
+ install --mode=0755 -D crontab ${pkgdir}/usr/bin/crontab
+ install --mode=0644 -D crontab.1 ${pkgdir}/usr/share/man/man1/crontab.1
+ gzip ${pkgdir}/usr/share/man/man1/crontab.1
+ install --mode=0644 -D crontab.5 ${pkgdir}/usr/share/man/man5/crontab.5
+ gzip ${pkgdir}/usr/share/man/man5/crontab.5
+}
diff --git a/cron.target b/cron.target
new file mode 100644
index 000000000000..580e7084904e
--- /dev/null
+++ b/cron.target
@@ -0,0 +1,5 @@
+[Unit]
+Description=Cron Jobs
+
+[Install]
+WantedBy=multi-user.target
diff --git a/crontab b/crontab
new file mode 100755
index 000000000000..5b95d32ae58f
--- /dev/null
+++ b/crontab
@@ -0,0 +1,95 @@
+#!/usr/bin/python2
+
+import tempfile
+import sys
+import os
+import argparse
+import getpass
+
+EDITOR = (os.environ.get('EDITOR') or
+ os.environ.get('VISUAL','/usr/bin/vim'))
+CRONTAB_DIR = '/var/spool/cron'
+
+args_parser = argparse.ArgumentParser(description='maintain crontab files for individual users')
+
+args_parser.add_argument('-u', '--user', type=str, dest='user', default=getpass.getuser(),
+ help='''It specifies the name of the user whose crontab is to be
+ tweaked. If this option is not given, crontab examines "your" crontab, i.e., the crontab of the person
+ executing the command. Note that su(8) can confuse crontab and that if you are running inside of su(8) you
+ should always use the -u option for safety's sake. The first form of this command is used to install a new
+ crontab from some named file or standard input if the pseudo-filename "-" is given.''')
+
+args_parser.add_argument('file', type=str, default='-', nargs='?')
+
+args_parser.add_argument('-l', '--list', dest='action', action='store_const', const='list',
+ help='''The current crontab will be displayed on standard output.''')
+
+args_parser.add_argument('-r', '--remove', dest='action', action='store_const', const='remove',
+ help='''The current crontab will be removed.''')
+
+args_parser.add_argument('-e', '--edit', dest='action', action='store_const', const='edit',
+ help='''This option is used to edit the current crontab using the editor
+ specified by the VISUAL or EDITOR environment variables. After
+ you exit from the editor, the modified crontab will be installed
+ automatically.''')
+
+args_parser.add_argument('-i', '--ask', dest='ask', action='store_true', default=False,
+ help='''This option modifies the -r option to prompt the user for a
+ 'y/Y' response before actually removing the crontab.''')
+
+#args_parser.add_argument('-s', '--secure', dest='secure', action='store_true', default=False,
+ #help='''It will append the current SELinux security context string as an
+ #MLS_LEVEL setting to the crontab file before editing / replacement occurs
+ #- see the documentation of MLS_LEVEL in crontab(5).''')
+
+def confirm(message):
+ while True:
+ answer = raw_input(message).lower()
+ if answer not in 'yn':
+ print('Please reply "y" or "n"')
+ continue
+
+ return answer == 'y'
+
+def list(cron_file, args):
+ with open(cron_file, 'r') as f:
+ sys.stdout.write(f.read())
+
+def remove(cron_file, args):
+ if not args.ask or confirm('Are you sure you want to delete %s (y/n)? ' % cron_file):
+ os.unlink(cron_file)
+
+def edit(cron_file, args):
+ with tempfile.NamedTemporaryFile() as tmp:
+ with open(cron_file, 'r') as inp:
+ tmp.file.write(inp.read())
+
+ tmp.file.flush()
+
+ if os.system("'%s' '%s'" % (EDITOR, tmp.name)) == 0:
+ tmp.file.seek(0)
+ with open(cron_file, 'w') as out:
+ out.write(tmp.file.read())
+
+def replace(cron_file, args):
+ infile = args.file
+ if infile == '-':
+ with open(cron_file, 'w') as out:
+ out.write(sys.stdin.read())
+
+ else:
+ with open(cron_file, 'w'), open(infile, 'r') as out, inp:
+ out.write(inp.read())
+
+if __name__ == '__main__':
+ args = args_parser.parse_args()
+ cron_file = os.path.join(CRONTAB_DIR, args.user)
+
+ action = {
+ 'list': list,
+ 'edit': edit,
+ 'remove': remove,
+ }.get(args.action, replace)
+
+ action(cron_file, args)
+
diff --git a/crontab.1 b/crontab.1
new file mode 100644
index 000000000000..18b04f1cae85
--- /dev/null
+++ b/crontab.1
@@ -0,0 +1,32 @@
+.TH CRONTAB 1 "2014-06-26" "" systemd-crontab-generator
+
+.SH NAME
+crontab - maintain crontab files for individual users
+
+.SH SYNOPSIS
+crontab [\-u user] file
+.br
+crontab [\-u user] [\-l | \-r | \-e] [\-i]
+
+.SH DESCRIPTION
+Crontab is the program used to let users install, deinstall or list
+recurrent jobs in the legacy cron format.
+.br
+Each user can have their own crontab, and though these are files in /var/spool/,
+they are not intended to be edited directly.
+.br
+These jobs are then automatically translated in systemd Timers & Units
+by systemd-crontab-generator.
+
+.SH FILES
+.TP
+.I /var/spool/cron
+Directory for users crontabs.
+
+.SH LIMITATIONS
+The \-s flag (SELinux) is not supported.
+.br
+/etc/cron.allow & /etc/cron.deny are not supported.
+
+.SH AUTHOR
+Alexandre Detiste <alexandre@detiste.be>
diff --git a/crontab.5 b/crontab.5
new file mode 100644
index 000000000000..40f17662ce28
--- /dev/null
+++ b/crontab.5
@@ -0,0 +1,377 @@
+.\"/* Copyright 1988,1990,1993,1994 by Paul Vixie
+.\" * All rights reserved
+.\" *
+.\" * Distribute freely, except: don't remove my name from the source or
+.\" * documentation (don't take credit for my work), mark your changes (don't
+.\" * get me blamed for your possible bugs), don't alter or remove this
+.\" * notice. May be sold if buildable source is provided to buyer. No
+.\" * warrantee of any kind, express or implied, is included with this
+.\" * software; use at your own risk, responsibility for damages (if any) to
+.\" * anyone resulting from the use of this software rests entirely with the
+.\" * user.
+.\" *
+.\" * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+.\" * I'll try to keep a version up to date. I can be reached as follows:
+.\" * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+.\" */
+.\"
+.\" $Id: crontab.5,v 2.4 1994/01/15 20:43:43 vixie Exp $
+.\"
+.TH CRONTAB 5 "03 July 2014"
+.UC 4
+.SH NAME
+crontab \- tables for driving cron
+.SH DESCRIPTION
+A
+.I crontab
+file contains instructions to the
+.IR cron (8)
+daemon of the general form: ``run this command at this time on this date''.
+Each user has their own crontab, and commands in any given crontab will be
+executed as the user who owns the crontab. Uucp and News will usually have
+their own crontabs, eliminating the need for explicitly running
+.IR su (1)
+as part of a cron command.
+.PP
+Blank lines and leading spaces and tabs are ignored. Lines whose first
+non-space character is a hash-sign (#) are comments, and are ignored.
+Note that comments are not allowed on the same line as cron commands, since
+they will be taken to be part of the command. Similarly, comments are not
+allowed on the same line as environment variable settings.
+.PP
+An active line in a crontab will be either an environment setting or a cron
+command. The crontab file is parsed from top to bottom, so any environment
+settings will affect only the cron commands below them in the file.
+An environment setting is of the form,
+.PP
+ name = value
+.PP
+where the spaces around the equal-sign (=) are optional, and any subsequent
+non-leading spaces in
+.I value
+will be part of the value assigned to
+.IR name .
+The
+.I value
+string may be placed in quotes (single or double, but matching) to preserve
+leading or trailing blanks. To define an empty variable, quotes
+.B must
+be used. The
+.I value
+string is
+.B not
+parsed for environmental substitutions or replacement of variables, thus lines
+like
+.PP
+ PATH = $HOME/bin:$PATH
+.PP
+will not work as you might expect. And neither will this work
+.PP
+ A=1
+ B=2
+ C=$A $B
+.PP
+There will not be any subsitution for the defined variables in the
+last value.
+.PP
+An alternative for setting up the commands path is using the fact that
+many shells will treat the tilde(~) as substitution of $HOME, so if you use
+.I bash
+for your tasks you can use this:
+.PP
+ SHELL=/bin/bash
+ PATH=~/bin:/usr/bin/:/bin
+.PP
+Several environment variables are set up automatically by the
+.IR cron (8)
+daemon.
+SHELL is set to /bin/sh, and LOGNAME and HOME are set from the /etc/passwd
+line of the crontab's owner. PATH is set to "/usr/bin:/bin".
+HOME, SHELL, and PATH may be overridden by settings in the crontab;
+LOGNAME is the user that the job is running from, and may not be changed.
+.PP
+(Another note: the LOGNAME variable is sometimes called USER on BSD systems...
+on these systems, USER will be set also.)
+.PP
+systemd-crontab-generator doesn't set the MAILTO variable nor send
+any mails like vixie-cron. The output of jobs is written in the journal.
+.\"In addition to LOGNAME, HOME, and SHELL,
+.\".IR cron (8)
+.\"will look at MAILTO if it has any reason to send mail as a result of running
+.\"commands in ``this'' crontab. If MAILTO is defined (and non-empty), mail is
+.\"sent to the user so named. MAILTO may also be used to direct mail to multiple
+.\"recipients by separating recipient users with a comma. If MAILTO is defined
+.\"but empty (MAILTO=""), no mail will be sent. Otherwise mail is sent to the
+.\"owner of the crontab.
+.\".PP
+.\"On the Debian GNU/Linux system, cron supports the
+.\".B pam_env
+.\"module, and loads the environment specified by
+.\".IR /etc/environment
+.\"and
+.\".IR /etc/security/pam_env.conf .
+.\"It also reads locale information from
+.\".IR /etc/default/locale .
+.\"However, the PAM settings do
+.\".B NOT
+.\"override the settings described above nor any settings in the
+.\".I crontab
+.\"file itself. Note in particular that if you want a PATH other than
+.\" "/usr/bin:/bin", you will need to set it in the crontab file.
+.\".PP
+.\"By default, cron will send mail using the mail "Content-Type:" header of
+.\" "text/plain" with the "charset=" parameter set to the charmap / codeset of the
+.\"locale in which
+.\".IR crond (8)
+.\"is started up - ie. either the default system locale, if no LC_* environment
+.\"variables are set, or the locale specified by the LC_* environment variables
+.\"( see
+.\".IR locale (7) ).
+.\"You can use different character encodings for mailed cron job output by
+.\"setting the CONTENT_TYPE and CONTENT_TRANSFER_ENCODING variables in crontabs,
+.\"to the correct values of the mail headers of those names.
+.PP
+The format of a cron command is very much the V7 standard, with a number of
+upward-compatible extensions. Each line has five time and date fields,
+followed by a command, followed by a newline character ('\\n').
+The system crontab (/etc/crontab) uses the same format, except that
+the username for the command is specified after the time and
+date fields and before the command. The fields may be separated
+by spaces or tabs.
+.PP
+Commands are executed by
+.IR cron (8)
+when the minute, hour, and month of year fields match the current time,
+.I and
+when at least one of the two day fields (day of month, or day of week)
+match the current time (see ``Note'' below).
+.IR cron (8)
+examines cron entries once every minute.
+The time and date fields are:
+.IP
+.ta 1.5i
+field allowed values
+.br
+----- --------------
+.br
+minute 0-59
+.br
+hour 0-23
+.br
+day of month 1-31
+.br
+month 1-12 (or names, see below)
+.br
+day of week 0-7 (0 or 7 is Sun, or use names)
+.br
+.PP
+A field may be an asterisk (*), which always stands for ``first\-last''.
+.PP
+Ranges of numbers are allowed. Ranges are two numbers separated
+with a hyphen. The specified range is inclusive. For example,
+8-11 for an ``hours'' entry specifies execution at hours 8, 9, 10
+and 11.
+.PP
+Lists are allowed. A list is a set of numbers (or ranges)
+separated by commas. Examples: ``1,2,5,9'', ``0-4,8-12''.
+.PP
+Step values can be used in conjunction with ranges. Following
+a range with ``/<number>'' specifies skips of the number's value
+through the range. For example, ``0-23/2'' can be used in the hours
+field to specify command execution every other hour (the alternative
+in the V7 standard is ``0,2,4,6,8,10,12,14,16,18,20,22''). Steps are
+also permitted after an asterisk, so if you want to say ``every two
+hours'', just use ``*/2''.
+.PP
+Names can also be used for the ``month'' and ``day of week''
+fields. Use the first three letters of the particular
+day or month (case doesn't matter). Ranges or
+lists of names are not allowed.
+.PP
+The ``sixth'' field (the rest of the line) specifies the command to be
+run.
+The entire command portion of the line, up to a newline
+.\" or % character
+, will be executed by /bin/sh or by the shell
+specified in the SHELL variable of the crontab file.
+.\"Percent-signs (%) in the command, unless escaped with backslash
+.\"(\\), will be changed into newline characters, and all data
+.\"after the first % will be sent to the command as standard
+.\"input. There is no way to split a single command line onto multiple
+.\"lines, like the shell's trailing "\\".
+.PP
+systemd-crontab-generator doesn't handle multi-line command split by
+the % character like vixie-cron.
+.PP
+Note: The day of a command's execution can be specified by two
+fields \(em day of month, and day of week. If both fields are
+restricted (i.e., aren't *), the command will be run when
+.I either
+field matches the current time. For example,
+.br
+``30 4 1,15 * 5''
+would cause a command to be run at 4:30 am on the 1st and 15th of each
+month, plus every Friday. One can, however, achieve the desired result
+by adding a test to the command (see the last example in EXAMPLE CRON FILE
+below).
+.PP
+Instead of the first five fields, one of eight special strings may appear:
+.IP
+.ta 1.5i
+string meaning
+.br
+------ -------
+.br
+@reboot Run once, at startup.
+.br
+@yearly Run once a year, "0 0 1 1 *".
+.br
+@annually (same as @yearly)
+.br
+@monthly Run once a month, "0 0 1 * *".
+.br
+@weekly Run once a week, "0 0 * * 0".
+.br
+@daily Run once a day, "0 0 * * *".
+.br
+@midnight (same as @daily)
+.br
+@hourly Run once an hour, "0 * * * *".
+.br
+.PP
+Please note that startup, as far as @reboot is concerned, is the time when
+the
+.IR cron (8)
+daemon startup. In particular, it may be before some system daemons,
+or other facilities, were startup. This is due to the boot order
+sequence of the machine.
+
+.SH EXAMPLE CRON FILE
+
+The following lists an example of a user crontab file.
+
+.nf
+
+# use /bin/bash to run commands, instead of the default /bin/sh
+SHELL=/bin/bash
+.\" # mail any output to `paul', no matter whose crontab this is
+.\" MAILTO=paul
+#
+# run five minutes after midnight, every day
+5 0 * * * $HOME/bin/daily.job >> $HOME/tmp/out 2>&1
+# run at 2:15pm on the first of every month
+.\" -- output mailed to paul
+15 14 1 * * $HOME/bin/monthly
+.\"# run at 10 pm on weekdays, annoy Joe
+.\"0 22 * * 1-5 mail \-s "It's 10pm" joe%Joe,%%Where are your kids?%
+23 0-23/2 * * * echo "run 23 minutes after midn, 2am, 4am ..., everyday"
+5 4 * * sun echo "run at 5 after 4 every sunday"
+# Run on every second Saturday of the month
+0 4 8-14 * * test $(date +\\%u) \-eq 6 && echo "2nd Saturday"
+.fi
+.SH EXAMPLE SYSTEM CRON FILE
+
+The following lists the content of a regular system-wide crontab file. Unlinke a
+user's crontab, this file has the username field, as used by /etc/crontab.
+
+.nf
+# /etc/crontab: system-wide crontab
+# Unlike any other crontab you don't have to run the `crontab'
+# command to install the new version when you edit this file
+# and files in /etc/cron.d. These files also have username fields,
+# that none of the other crontabs do.
+
+SHELL=/bin/sh
+PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
+
+# m h dom mon dow user command
+17 * * * * root cd / && run-parts \-\-report /etc/cron.hourly
+25 6 * * * root test \-x /usr/sbin/anacron || ( cd / && run-parts \-\-report /etc/cron.daily )
+47 6 * * 7 root test \-x /usr/sbin/anacron || ( cd / && run-parts \-\-report /etc/cron.weekly )
+52 6 1 * * root test \-x /usr/sbin/anacron || ( cd / && run-parts \-\-report /etc/cron.monthly )
+#
+.fi
+.SH SEE ALSO
+cron(8), crontab(1)
+.SH EXTENSIONS
+When specifying day of week, both day 0 and day 7 will be considered Sunday.
+BSD and AT&T seem to disagree about this.
+.PP
+Lists and ranges are allowed to co-exist in the same field. "1-3,7-9" would
+be rejected by AT&T or BSD cron -- they want to see "1-3" or "7,8,9" ONLY.
+.PP
+Ranges can include "steps", so "1-9/2" is the same as "1,3,5,7,9".
+.PP
+Months or days of the week can be specified by name.
+.PP
+Environment variables can be set in the crontab. In BSD or AT&T, the
+environment handed to child processes is basically the one from /etc/rc.
+.PP
+.\"Command output is mailed to the crontab owner (BSD can't do this), can be
+.\"mailed to a person other than the crontab owner (SysV can't do this), or the
+.\"feature can be turned off and no mail will be sent at all (SysV can't do this
+.\"either).
+.\".PP
+All of the `@' commands that can appear in place of the first five fields
+are extensions.
+.SH LIMITATIONS
+The
+.I cron
+daemon runs with a defined timezone. It currently does not support
+per-user timezones. All the tasks: system's and user's will be run based on the
+configured timezone. Even if a user specifies the
+.I TZ
+environment variable in his
+.I crontab
+this will affect only the commands executed in the crontab, not the execution
+of the crontab tasks themselves.
+
+The
+.I crontab
+syntax does not make it possible to define all possible periods one could
+image off. For example, it is not straightforward to define the last
+weekday of a month. If a task needs to be run in a specific period of time
+that cannot be defined in the
+.I crontab
+syntaxs the best approach would be to have the program itself check the
+date and time information and continue execution only if the period
+matches the desired one.
+
+If the program itself cannot do the checks then a wrapper script would be
+required. Useful tools that could be used for date analysis are
+.I ncal
+or
+.I calendar
+For example, to run a program the last Saturday of every month you could use
+the following wrapper code:
+
+.nf
+0 4 * * Sat [ "$(date +\\%e)" = "`ncal | grep $(date +\\%a | sed \-e 's/.$//') | sed \-e 's/^.*\\s\\([0-9]\\+\\)\\s*$/\\1/'`" ] && echo "Last Saturday" && program_to_run
+.fi
+
+.B systemd-crontab-generator
+doesn't support these
+.B vixie-cron
+features:
+.TP
+*
+sending emails with job output, the output is written to systemd journal
+.TP
+*
+multi-line jobs separated by the '%' character
+.TP
+*
+vixie-cron requires that each entry in a crontab end in a newline character. If the
+last entry in a crontab is missing a newline (ie, terminated by EOF), vixie-cron will
+consider the crontab (at least partially) broken.
+.br
+systemd-crontab-generator considers this crontab as valid
+
+.\".SH DIAGNOSTICS
+
+.SH AUTHOR
+Paul Vixie <paul@vix.com> is the author of
+.I cron
+and original creator of this manual page. This page has also been modified for
+Debian by Steve Greenland, Javier Fernandez-Sanguino and Christian Kastner.
+
diff --git a/systemd-crontab-generator b/systemd-crontab-generator
new file mode 100755
index 000000000000..0f6408b516e9
--- /dev/null
+++ b/systemd-crontab-generator
@@ -0,0 +1,301 @@
+#!/usr/bin/python2
+import sys
+import pwd
+import os
+import re
+
+def files(dirname):
+ try:
+ return filter(os.path.isfile, map(lambda f: os.path.join(dirname, f), os.listdir(dirname)))
+ except OSError:
+ return []
+
+envvar_re = re.compile(r'^([A-Za-z_0-9]+)\s*=\s*(.*)$')
+
+CRONTAB_FILES = ['/etc/crontab'] + files('/etc/cron.d')
+ANACRONTAB_FILES = ['/etc/anacrontab']
+USERCRONTAB_FILES = files('/var/spool/cron')
+
+TARGER_DIR = sys.argv[1]
+TIMERS_DIR = os.path.join(TARGER_DIR, 'cron.target.wants')
+SELF = os.path.basename(sys.argv[0])
+
+MINUTES_SET = range(0, 60)
+HOURS_SET = range(0, 24)
+DAYS_SET = range(0, 32)
+DOWS_SET = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
+MONTHS_SET = range(0, 13)
+
+ROOT_USER = pwd.getpwnam('root')
+
+try:
+ os.makedirs(TIMERS_DIR)
+except OSError as e:
+ if e.errno != os.errno.EEXIST:
+ raise
+
+def parse_crontab(filename, withuser=True, monotonic=False):
+ basename = os.path.basename(filename)
+ environment = {
+ 'SHELL': '/bin/sh',
+ 'PATH': '/usr/bin:/bin',
+ }
+ with open(filename, 'r') as f:
+ for line in f.readlines():
+ if line.startswith('#'):
+ continue
+
+ line = line.rstrip('\n')
+ envvar = envvar_re.match(line)
+ if envvar:
+ environment[envvar.group(1)] = envvar.group(2)
+ continue
+
+ parts = line.split()
+ line = ' '.join(parts)
+
+ if monotonic:
+ if len(parts) < 4:
+ continue
+
+ period, delay, jobid = parts[0:3]
+ command = ' '.join(parts[3:])
+ period = {
+ '1': 'daily',
+ '7': 'weekly',
+ '@midnight': 'daily'
+ }.get(period, None) or period.lstrip('@')
+
+ environment['LOGNAME'] = environment['USER'] = 'root'
+ environment['HOME'] = ROOT_USER.pw_dir
+ yield {
+ 'e': ' '.join('"%s=%s"' % kv for kv in environment.iteritems()),
+ 's': environment['SHELL'],
+ 'l': line,
+ 'f': filename,
+ 'p': period,
+ 'd': delay,
+ 'j': jobid,
+ 'c': command,
+ 'u': 'root'
+ }
+
+ else:
+ if line.startswith('@'):
+ if len(parts) < 2:
+ continue
+
+ period = parts[0]
+ period = {
+ '1': 'daily',
+ '7': 'weekly',
+ '@midnight': 'daily'
+ }.get(period, None) or period.lstrip('@')
+
+ user, command = (parts[1], ' '.join(parts[2:])) if withuser else (basename, ' '.join(parts[1:]))
+
+ environment['LOGNAME'] = environment['USER'] = user
+ environment['HOME'] = pwd.getpwnam(user).pw_dir
+
+ yield {
+ 'e': ' '.join('"%s=%s"' % kv for kv in environment.iteritems()),
+ 's': environment['SHELL'],
+ 'l': line,
+ 'f': filename,
+ 'p': period,
+ 'u': user,
+ 'c': command
+ }
+ else:
+ if len(parts) < 6 + int(withuser):
+ continue
+
+ minutes, hours, days = parts[0:3]
+ months, dows = parts[3:5]
+ user, command = (parts[5], ' '.join(parts[6:])) if withuser else (basename, ' '.join(parts[5:]))
+
+ environment['LOGNAME'] = environment['USER'] = user
+ environment['HOME'] = pwd.getpwnam(user).pw_dir
+
+ yield {
+ 'e': ' '.join('"%s=%s"' % kv for kv in environment.iteritems()),
+ 's': environment['SHELL'],
+ 'l': line,
+ 'f': filename,
+ 'm': parse_time_unit(minutes, MINUTES_SET),
+ 'h': parse_time_unit(hours, HOURS_SET),
+ 'd': parse_time_unit(days, DAYS_SET),
+ 'w': parse_time_unit(dows, DOWS_SET, dow_map),
+ 'M': parse_time_unit(months, MONTHS_SET, month_map),
+ 'u': user,
+ 'c': command
+ }
+
+def parse_time_unit(value, values, mapping=int):
+ if value == '*':
+ return ['*']
+ return sorted(list(reduce(lambda a, i: a.union(set(i)), map(values.__getitem__,
+ map(parse_period(mapping), value.split(','))), set())))
+
+def month_map(month):
+ try:
+ return int(month)
+ except ValueError:
+ return ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'nov', 'dec'].index(month.lower()[0:3]) + 1
+
+def dow_map(dow):
+ try:
+ return ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'].index(dow[0:3].lower())
+ except ValueError:
+ return int(dow) % 7
+
+def parse_period(mapping=int):
+ def parser(value):
+ try:
+ range, step = value.split('/')
+ except ValueError:
+ value = mapping(value)
+ return slice(value, value + 1)
+
+ if range == '*':
+ return slice(None, None, int(step))
+
+ try:
+ start, end = range.split('-')
+ except ValueError:
+ return slice(mapping(range), None, int(step))
+
+ return slice(mapping(start), mapping(end), int(step))
+
+ return parser
+
+def generate_timer_unit(job, seq):
+ n = next(seq)
+ unit_name = "cron-%s-%s" % (job['u'], n)
+
+ if 'p' in job:
+ if job['p'] == 'reboot':
+ schedule = 'OnBootSec=%sm' % job.get('d', 5)
+ else:
+ try:
+ schedule = 'OnCalendar=*-*-1/%s 0:%s:0' % (int(job['p']), job.get('d', 0))
+ except ValueError:
+ schedule = 'OnCalendar=%s' % job['p']
+
+ accuracy = job.get('d', 1)
+
+ else:
+ dows = ','.join(job['w'])
+ dows = '' if dows == '*' else dows + ' '
+
+ schedule = 'OnCalendar=%s*-%s-%s %s:%s:00' % (dows, ','.join(map(str, job['M'])),
+ ','.join(map(str, job['d'])), ','.join(map(str, job['h'])), ','.join(map(str, job['m'])))
+ accuracy = 1
+
+ with open('%s/%s.timer' % (TARGER_DIR, unit_name), 'w') as f:
+ f.write('''# Automatically generated by %s
+# Source crontab: %s
+
+[Unit]
+Description=[Cron] "%s"
+PartOf=cron.target
+RefuseManualStart=true
+RefuseManualStop=true
+
+[Timer]
+Unit=%s.service
+Persistent=true
+AccuracySec=%sm
+%s
+''' % (SELF, job['f'], job['l'], unit_name, accuracy, schedule))
+
+ try:
+ os.symlink('%s/%s.timer' % (TARGER_DIR, unit_name), '%s/%s.timer' % (TIMERS_DIR, unit_name))
+ except OSError as e:
+ if e.errno != os.errno.EEXIST:
+ raise
+
+ with open('%s/%s.service' % (TARGER_DIR, unit_name), 'w') as f:
+ f.write('''# Automatically generated by %s
+# Source crontab: %s
+
+[Unit]
+Description=[Cron] "%s"
+RefuseManualStart=true
+RefuseManualStop=true
+
+[Service]
+Type=oneshot
+User=%s
+Environment=%s
+ExecStart=%s -c '%s'
+''' % (SELF, job['f'], job['l'], job['u'], job['e'], job['s'], job['c']))
+
+ return '%s.timer' % unit_name
+
+def generate_path_unit():
+ combinedcronfiles = [ '/etc/crontab', '/etc/cron.d', '/etc/anacrontab', '/var/spool/cron' ]
+ with open('%s/systemd-crontab-update.path' % (TARGER_DIR), 'w') as f:
+ f.write('''# Automatically generated by %s
+
+[Unit]
+Description=[Cron] Update cron units
+RefuseManualStart=true
+RefuseManualStop=true
+
+[Path]
+%s
+''' % (SELF,'\n'.join([ "PathChanged="+f for f in combinedcronfiles ]) ))
+
+ try:
+ os.symlink('%s/systemd-crontab-update.path' % (TARGER_DIR), '%s/systemd-crontab-update.path' % (TIMERS_DIR))
+ except OSError as e:
+ if e.errno != os.errno.EEXIST:
+ raise
+
+ with open('%s/systemd-crontab-update.service' % (TARGER_DIR), 'w') as f:
+ f.write('''# Automatically generated by %s
+
+[Unit]
+Description=[Cron] Update cron units
+
+[Service]
+Type=oneshot
+ExecStart=/usr/bin/systemd-crontab-update
+''' % (SELF))
+
+ return "systemd-crontab-update.path"
+
+seqs = {}
+def count():
+ n = 0
+ while True:
+ yield n
+ n += 1
+
+
+for filename in CRONTAB_FILES:
+ try:
+ for job in parse_crontab(filename, withuser=True):
+ generate_timer_unit(job, seqs.setdefault(job['u'], count()))
+ except IOError:
+ pass
+
+for filename in ANACRONTAB_FILES:
+ try:
+ for job in parse_crontab(filename, monotonic=True):
+ generate_timer_unit(job, seqs.setdefault(job['u'], count()))
+ except IOError:
+ pass
+
+for filename in USERCRONTAB_FILES:
+ try:
+ for job in parse_crontab(filename, withuser=False):
+ generate_timer_unit(job, seqs.setdefault(job['u'], count()))
+ except IOError:
+ pass
+
+try:
+ generate_path_unit()
+except IOError:
+ pass
diff --git a/systemd-crontab-generator.1 b/systemd-crontab-generator.1
new file mode 100644
index 000000000000..a3c63e6037bf
--- /dev/null
+++ b/systemd-crontab-generator.1
@@ -0,0 +1,53 @@
+.TH SYSTEMD-CRONTAB-GENERATOR 1 "2014-06-29" "" systemd-crontab-generator
+
+.SH NAME
+systemd-crontab-generator - translate cron schedules in systemd Units
+
+.SH SYNOPSIS
+systemd-crontab-generator folder
+
+.SH DESCRIPTION
+systemd-crontab-generator translates the legacy cron files (see FILES)
+into systemd units & timers.
+.PP
+It should be run
+.TP
+*
+at boot,
+.TP
+*
+after each manual updates to the cron files,
+.TP
+*
+and when distribution packages add files in /etc/cron.d/.
+.PP
+\fBsystemd-crontab-update\fR is a sample script that
+calls systemd-crontab-generator.
+
+.SH FILES
+.TP
+.B /etc/crontab
+System crontab.
+
+.TP
+.B /etc/cron.d
+Directory for system crontabs.
+
+.TP
+.B /etc/anacrontab
+
+.TP
+.B /var/spool/cron
+Directory for users crontabs.
+.br
+
+.\.SH LIMITATIONS
+.\The \-s flag (SELinux) is not supported.
+.\.br
+.\/etc/cron.allow & /etc/cron.deny are not supported.
+
+.SH SEE ALSO
+\fBcrontab\fR(1),\fBsystemd.unit\fR(5),\fBsystemd.timer\fR(5)
+
+.SH AUTHOR
+Alexandre Detiste <alexandre@detiste.be>
diff --git a/systemd-crontab-update b/systemd-crontab-update
new file mode 100755
index 000000000000..96fe3f5b2aa7
--- /dev/null
+++ b/systemd-crontab-update
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+systemctl daemon-reload
+systemctl try-restart cron.target