bcron documentation: Top

[Top] | [Contents] | [Index] | [ ? ]

# bcron documentation

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

# 1. Introduction

Name comes from: Bruce's / Better / Busy cron.

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

## 1.1 Problems With Other cron Systems

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

### 1.1.1 vixie-cron

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

### 1.1.2 fcron

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

### 1.1.3 anacron

Anacron is only really useful for running jobs once a day or less frequently. From what I've seen, it's good at what it does, just not useful at much else.

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

### 1.1.4 dcron

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

## 1.2 Requirements

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

## 1.3 Design Choices

Use local socket to submit files.

  There are two basic methods of submitting crontab files:

  1. Use a setuid program to write directly into spool files.
  2. Set up a local socket to submit jobs to a daemon.

  (1) Using a setuid submission agent was discarded to prevent the possibility of all the bugs that have plagued other setuid submission agents. The socket protocol is deliberately very simple, to make the submission agent foolproof.

Multiple process daemon.

  By seperating job submission from job execution, exploiting the system to run arbitrary jobs as privileged users is made even harder. It also makes the design of those individual programs much simpler.

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

## 1.4 vixie-cron Patches

This section lists all the non-trivial patches found for vixie-cron, what problem they appear to address, and (if appropriate) how bcron will avoid the same problem. The patches listed come from multiple sources, including the latest RPM (Fedora Core IIRC).

0days.patch

  This patch modifies the ``crontab.5'` man page to remove allowing ``0'` for day of month or month numbers.

badsig.patch

  On some systems, signal handlers are single-shot. This patch modifies the SIGHUP handler to reactivate itself before it returns. bcron will use the bglibs signal functions, which use sigaction to create suitable handlers, where appropriate. bcron doesn't use signals for any purpose.

buffer.patch

  This patch increases the maximum username length from 20 to 32, and modifies calls to strcpy to use strncpy to ensure all string copies are length bounded. bcron uses dynamically allocated strings to eliminate the possibility of buffer overflows.

close_stdin.diff

  This patch modifies the cron daemon to close stdin, stdout, and stderr on startup, and to reopen them as ``/dev/null'`. The bcron daemons run under supervise, and have no need of such handling.

crond.patch

  Adds support for ``/etc/cron.d'`

cront_stdin.patch

  Appears to modify ``crontab'`'s command-line handling such that no argument is interpreted as to read the crontab from standard input.

crontab.5.diff

  Documents several builtin macros to replace the first 5 fields. This macros consist of: ``@reboot'`, ``@yearly'`, ``@annually'`, ``@monthly'`, ``@weekly'`, ``@daily'`, ``@midnight'`, and ``@hourly'`. bcron will not, at least initially, support these macros.

crontab.patch

  Modifies crontab to use strncpy and snprintf when writing into length-bounded strings.

crontabloc.patch

  Patches the crontab man page to reference ``/etc/crontab'`.

dst.patch

  Patches the crontab man page to point out that DST may cause jobs to be skipped or repeated.

name.patch

  Appears to modify how the cron daemon handles sending messages to syslog. bcron will log messages to stderr, avoiding syslog entirely.

nodot.patch

  Adds ``-i'` to the list of arguments sent to sendmail (result is ``-FCronDaemon -i -odi -oem'`). Only useful for sendmail, but still needed.

root_-u-85879.patch

  Sanity checks the use of ``-u'` against UID and/or root.

security2.patch

  Does some sanity checking on mailto, and does a setuid before sending mail. bcron plays safe with mailto by putting it into a message header, and always drops root privileges before executing commands.

sigchld.patch

  Return the SIGCHLD handler to its default state before executing commands.

sprintf.patch

  More sprintf -> snprintf conversions.

time.patch

  Sync all the crontabs before sleeping to handle changes in the system time.

timeaftertime.patch

  The previous patch created double execution issues with small backwards adjustments in the clock time.

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

# 2. Design Notes

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

## 2.1 Fundamental Operations

The following is a list of all the "core" operations that must be provided based on the requirements.

Execute jobs

  Strictly speaking, this is the only role that requires superuser privileges. Every other job should run as non-root.

Schedule jobs

  Scan all the known jobs, determine which one needs to be executed next, and sleep until that time arrives.

Accept new user crontabs

  Listen for connections on a socket and accept job data.

Parse crontabs into internal job format

  Read the submitted files and parse their contents into a structured format.

Check for new system crontabs

  Check ``/etc/crontab'` and ``/etc/cron.d/*'` every minute for modifications. If any files are changed, added, or deleted, add the listed jobs. On systems with tightened security, these files may only be readable by ``root'`.

Manage saved state

  All jobs need to be saved to disk along with when they were last executed, in order to determine when they should be next executed.

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

## 2.2 Programs

``bcron-sched'`

  Top-level scheduler agent. Starts up as root, runs ``bcron-exec'`, and then drops root permanently.

``bcron-exec'`

  Accepts jobs to run from ``bcron-sched'` on stdin, writes exit status back to stdout.

``bcron-spool'`

  Manages the cron spool: receives jobs submitted from users, writes them to files in ``/var/spool/cron/crontabs'`, and notifies ``bcron-sched'`. This needs to be run from ``unixserver'` in order to securely determine the invoking UID. This program will optionally run the file through an external filter, specified on the command line, before installing the job.

``bcron-update'`

  Watches for changes to the system crontabs and notifies ``bcron-sched'`.

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

## 2.3 Files

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

### 2.3.1 File Hierarchy

``/etc/cron.d/'`
``/etc/cron.d/*'`
``/etc/crontab'`

  The above three items are read

``/var/spool/cron/'`
``/var/spool/cron/crontabs/'`

  Directory containing raw (text) crontab files.

``/var/spool/cron/crontabs/:etc:cron.d:*'`

  Colon is chosen as a seperator because usernames cannot contain colons due to the format of ``/etc/passwd'`.

``/var/spool/cron/crontabs/:etc:crontab'`
``/var/spool/cron/bcrontabs/'`

  Directory containing pre-parsed (aka compiled) crontab files (Not yet implemented).

``/var/spool/cron/tmp/'`

  Temporary directory for files as they are written.

``/var/spool/cron/trigger'`

  Named pipe used to tell ``bcron-sched'` to rescan the crontabs.

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

## 2.4 Inter-Process Communication

All communication between programs is done in terms of either "packets" or "lines". A packet is formatted as a netstring. That is, a packet of length N is encoded as the ASCII decimal value of N, ``:'`, N bytes of data, terminated by ``,'`. A line is simply a series of non-NUL bytes terminated by a NUL byte.

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

### 2.4.1 Job Submission Protocol

Client sends a packet containing a single byte command followed by the username. If the command requires additional data, it is seperated from the username by a NUL byte. Server responds with a packet containing a response byte followed by a text message.

Client command codes are:

`S`

  Submit a user crontab file. The content string contains the entire crontab file.

`L`

  List the current crontab file. No content string.

`R`

  Remove any previously submitted crontab file. No content string.

`Y`

  List all system crontabs. No content string. This command is only available to users ``root'` and ``cron'`.

Server response codes are:

`K`

  Command was successful; file was parsed and accepted.

`D`

  File could not be parsed.

`Z`

  Temporary internal error.

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

### 2.4.2 bcron-exec Protocol

Input packets contain a series of four or more NUL-terminated lines:

`ID`
`username`
`command`
`environment`

  The environment is optional. If the environment contains `SHELL`, it replaces the default shell (``/bin/sh'`). If the environment contains `MAILTO`, it overrides the default mailing address derived from the username.

Output packet:

`ID`
`NUL`
`response code`
`text message`

Output packets are sent asynchronously with respect to input packets.

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

# 3. Configuration

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

## 3.1 Environment Variables

`BCRON_SPOOL`

  The base directory for bcron's files. Defaults to ``/var/spool/cron'`.

`BCRON_USER`

  The non-root user name to switch to for all processes that don't require root privileges. Defaults to ``cron'`.

`BCRON_MAXSIZE`

  The maximum size (in bytes) of a single user crontab. Defaults to unlimited.

`BCRON_MAXJOBS`

  The maximum number of jobs in a single user crontab. Defaults to unlimited.

`BCRON_SOCKET`

  The full path to the UNIX-domain socket used to submit crontabs. Defaults to ``/var/run/bcron-spool'`.

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

# 4. Implementation Notes

[ < ] | [ > ] |  | [ << ] | [ Up ] | [ >> ] |  |  |  |  | [Top] | [Contents] | [Index] | [ ? ]

## 4.1 Job Scheduler

Getting the job scheduler to work correctly for all possible cases consumed more time than all the other parts of the program put together, and this is all because of the problems that daylight savings causes.

The ultimate goal is this: given a linear timestamp, determine the next linear timestamp after which a job must be run. "Linear time" means the number of seconds since an epoch, in this case the UNIX epoch, midnight GMT January 1st, 1970. To contrast, "local time" means the time in terms of years, months, days, hours, minutes, and seconds in the current locale.

The time specification for jobs is composed of a set of local time bitmaps quantifying under what time conditions the job should run, assuming the conditions are evaluated every minute. The maximum scheduling resolution is one minute.

The effective algorithm is to step through every possible minute until the local time matches all the conditions in the time specification. This would result in an algorithm that could take up to 527,040-1 steps to complete, which is far too big a number to evaluate on every job. So, the algorithm optimizes away all impossible cases to make much larger time jumps. For example, if the job cannot be scheduled on the current day, skip over the entire day instead of just the current minute.

There are two ways I approached this task. First, do all calculations in terms of local time, and return the linear time at the last step. Second, do as many calculations as possible in terms of linear time, which can be returned directly. Both methods start with the same input data: The current linear time, and a set of bitmasks representing under what time conditions the job should run.

The first method was the most straightforward to get mostly working, until I started to consider the implications of DST. During the transition from normal time to DST, an hour is skipped. This is no big deal, as `mktime` will (or can be made to) compensate by picking the next valid hour when presented with a "missing" hour. On the other hand, there are many gotchas when dealing with the duplicated hour. For example, hourly jobs need to get scheduled on both, but daily jobs on only one.

The second method was harder to get working initially, as the math is more complicated. Despite doing many calculations in terms of linear time, this method still needs to keep track of the local time, in order to check against the bitmaps as well as to determine things like when the next day or month should start. This approach proved to be much easier to work with, once the initial math was done, and easier to make work correctly with regards to DST transitions.

[Top] | [Contents] | [Index] | [ ? ]

# Footnotes

### (1)

There are actually others, but these two are the most simple and portable of the choices. See also http://cr.yp.to/docs/secureipc.html

[Top] | [Contents] | [Index] | [ ? ]

# Table of Contents

[Top] | [Contents] | [Index] | [ ? ]

# About This Document

This document was generated by Bruce Guenter on June, 8 2005 using texi2html 1.76.

The buttons in the navigation panels have the following meaning:

Button | Name | Go to | From 1.2.3 go to
--- | --- | --- | ---
[ < ] | Back | previous section in reading order | 1.2.2
[ > ] | Forward | next section in reading order | 1.2.4
[ << ] | FastBack | beginning of this chapter or previous chapter | 1
[ Up ] | Up | up section | 1.2
[ >> ] | FastForward | next chapter | 2
[Top] | Top | cover (top) of document |
[Contents] | Contents | table of contents |
[Index] | Index | index |
[ ? ] | About | about (help) |

where the Example assumes that the current position is at Subsubsection One-Two-Three of a document of the following structure:

- 1. Section One
  - 1.1 Subsection One-One
    - ...
  - 1.2 Subsection One-Two
    - 1.2.1 Subsubsection One-Two-One
    - 1.2.2 Subsubsection One-Two-Two
    - 1.2.3 Subsubsection One-Two-Three <== Current Position
    - 1.2.4 Subsubsection One-Two-Four
  - 1.3 Subsection One-Three
    - ...
  - 1.4 Subsection One-Four

This document was generated by Bruce Guenter on June, 8 2005 using texi2html 1.76.