Skip to content
/ schedl Public

Static scheduler written in Scheme, with support for missed jobs

License

Notifications You must be signed in to change notification settings

matthmr/schedl

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

19 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Schedl

schedl - Static scheduler

Simple static scheduler, with support for missed scheduled jobs using statx(2). guile/scheme version of sched.gpl, simplified and less configurable.

Building

There’s no need to manually compile this project, as it is Scheme code, which is AOT-compiled by guile. The Makefile does provide some targets:

  • install: install system-wide
  • test: runs the test suite

and some variables:

  • PREFIX: install prefix (/usr/local/)
  • INFODIR: info prefix, relative to PREFIX (${PREFIX}/share/info/dir/)
  • DOCS: y or n for compiling and installing documentation (y)

Again, you can use the script as is, without needing to install it. It’s recomended you run the test suite, however.

You can pinpoint errors by running make tests or make failed-tests.

Using

Run schedl [options] <jobfile>.

Where <jobfile> is a required job file.

See schedl --help.

jobfile syntax

jobfiles are scheme files that contain jobs to run on a time scope. A time scope should be any combination of Day-Month-Year and/or Day-Month scopes.

Each job expression is defined with the job macro of the following form:

(job <time-scope> <jobs>)

Time scope

Time scopes are scheme lists that serve as the first argument for a job expression. The main time scopes are of the form:

Day-Month scope

You can define a job to run given a month and a day using the syntax (<day> <month>). For example:

  • (14 3): March 14th
  • (9 10): October 9th
  • (1 1): January 1st
Week-as-Day syntax

You can substitute the day field by a week field. Those are sun, mon, tue, wed, thu, fri and sat. For example:

  • (sat 1): Every Saturday of January
  • (mon 5): Every Monday of May
  • (sun 12): Every Sunday of December
Verbose-Month syntax

You can substitute a numeric month with a verbose month, with a three letter abbreviation. For example:

  • (sat jan): Every Saturday of January
  • (mon may): Every Monday of May
  • (sun dec): Every Sunday of December
Day-Month-Year scope

You can define a job to run given an year, a month and a day using the syntax (<day> <month> <year>). For example:

  • (6 5 2023): May 6th of 2023
  • (1 1 1970): January 1st of 1970
  • (19 1 2038): January 19th of 2038

Matching expressions

In place of a literal field, you can use a matching expression that will match the certain element given some expression boundaries.

match-any-field

A * token matches any value of the field it’s in. For example:

  • (* 3): Every day of March
  • (14 *): Every 14th day of every month
  • (* *): Every day of every month
match-any-predicate

A 1-nested list makes the job match in any of its predicates. It has the syntax ((<expression> ...)), where <expression> is a element of a job expression. For example:

  • ((25 31) dec): Every December 25th or 31st
  • (1 (jan dec)): Every 1st of January or 1st of December
  • ((1 10) (mar aug)): Every 1st of March, 1st of August, 10th of March or 10th of August
match-range-field

A - expression matches a value of a field whose LHS and RHS are the lower and upper bounds respectively. It has the syntax (- <lower-bound> <upper-bound> <step>?). For example:

  • (* (- jan mar)): Every day of January, February or March
  • ((- 1 20) aug): Every day of August from 1 to 20
  • ((- 10 20) (- jan aug)): Every day from January to August, from 10 to 20

A third field indicates a step. For example:

  • ((- * * 3) 1): Every three days in January, starting from the 1st day
  • ((- 10 * 2) *): Every two days in every month, starting from the 10th day

You can omit fields with *. Range fields can also be elements of match-any-predicate, for example:

  • ((1 (- 10 20)) jan): Every 1st of January, or 10th through 20th of January

The maximum nesting of match-any-predicate is 2. Meaning the example above shows the deepest you can nest matchers.

If you omit the LHS, it’s assumed to be the primary of your field:

  • January in the month field
  • day 1 in the numeric day field
  • Sunday in the week day field

If LHS is omitted, it won’t support step, unless RHS is also omitted.

If you omit the RHS, it’s assumed to be the last of your field:

  • December in the month field
  • day 31, 30 or 28 in the numeric day field (depending on the current matched month)
  • Saturday in the week day field

The year field has no primaries nor lasts.

Job identification

A job can be identified as the third argument of a job expression, after the time scope. Jobs are lists with job-scopes as elements.

For example:

(job (25 12) ((christmas "It's christmas")))

A job-scope is of the form:

(<job-name> <job-description>)

where job-description is a string. It can also have this form:

(<job-name> <job-description> . <job-execve>)

Where job-execve is an execve(2) compatible string. For example:

(job (* *) ((every-day "Script to run everyday" . "echo hello world")))

If a time-scope matches a job, then the job name and description will be written to STDOUT following the following format:

JOB: <job-name> <job-description>

or, in the case job-execve is provided:

DO: <job-execve>
JOB: <job-name> <job-description>

For compatibility’s sake, it’s recomended to have it so that job-name is a valid file name in your file system.

You can use something like a shell script to add actions to these jobs (using while read and REGEXPS, for example). In the project repository root, there’s a file called utils/schedl-handler-example.sh, which you can follow the comments and edit it to get an example handler.

Schedl invocation

There aren’t many options on schedl. The only one that actually matters is -k: if schedl is called with this option, then it won’t update the jobfile.

How it works

schedl will check the mtime of the file and take that as the last time the file did its jobs. It will then see how many jobs happened between then and now.

If given the --dry or -k flag, it will not update the mtime after accessing the file. The default behavior is to update it.

In the project repository root, there’s a file called utils/edit-jobfile.sh. You can use it to edit the jobfile without changing the way schedl would interpret it. Invoke it like:

./utils/edit-jobfile.sh jobfile editor

where editor is an optional argument, you can also pass it as the environment varibale EDITOR.

Working with schedl-handler-example.sh

NOTE: If you want to write your own implementation for schedl, feel free to ignore this section.

In the case you want to use the default example as your frontend for schedl, here are some things you need to consider:

  • job descriptions may not start with a ! character:
    • (job ... ((foo "foo bar"))) is fine, (job ... ((foo "!foo bar"))) isn’t
  • job names may be valid file system file names:
    • (job ... ((foo-bar ...))) is fine, (job ... ((foo/bar ...))) isn’t
  • job names may not end with .sh; that would make future handlers that look for the executable part of your job confused:
    • (job ... ((foosh ...))) is fine, (job ... ((foo.sh ...))) isn’t
  • job names may not end with .log; that would make future handlers that look for logs:
    • (job ... ((foolog ...))) is fine, (job ... ((foo.log ...))) isn’t

The way this handler works (at least on my system), is that it gets called as soon as the user logs in, then another script looks for files under /tmp/schedl, and it either prints it, or prompts for execution. The file /tmp/schedl/session-lock prevents schedl from running again. It’s not created in this script, it should be created on the one that handles the files created by this script (which I didn’t added to this repository because it doesn’t make sense to be here, but if you want to see an example, see: https://github.com/matthmr/scripts/blob/master/x/xsession.)

About

Static scheduler written in Scheme, with support for missed jobs

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published