# Using `at` for one time jobs with really high granularity



## tuaris (Feb 4, 2019)

I'm putting together a service script using `/usr/libexec/atrun` where it would monitor a directory (or run `atq`) for incoming 'job' files, move the file to a 'running' directory, execute the job, then finally remove the file.   The idea here is that instead of polling for jobs (how it's currently done with the cron approach) this would be instantaneous.

The monitoring script, forking the processes, and keeping track of the states is functional.  I already have it in use for another solution.  The part I'm stuck on is how to point `at` and `atrun` to a custom directory other than /var/at/jobs so that it does not interfier with the crontab version of it.


----------



## olli@ (Feb 5, 2019)

I'm afraid the standard `at` and `atrun` programs don't provide a way to specify the directory for job files.

However, in the case described above I would not use at/atrun, but use a simple loop to poll the directory. Assuming you use `/bin/sh`, it would look something like this:

```
#!/bin/sh
JOB_DIR="/your/job/directory"
LAST_MTIME=0
while :; do
        NEW_MTIME=$(stat -f '%Fm' "$JOB_DIR")
        if [ $NEW_MTIME != $LAST_MTIME ]; then
                # ... look for new jobs and handle them ...
                LAST_MTIME=$NEW_MTIME
        else
                sleep 60    # ... or whatever granularity you want
        fi
done
```
It works by looking at the so-called mtime (modification time) of the directory. This mtime is updated each time the directory contents change (e.g. a file is added or removed). Checking the mtime is more efficient than retrieving a directory listing, and efficiency is important if you poll it often.

A much better way would be to not use polling at all, but get notified whenever the directory changes. The two advantages are: (a) you don't have to check the directory regularly, and (b) your script will react instantaneously, i.e. within fractions of a second. This can be done with FreeBSD's kqueue(2) feature. To use it from a shell script, you can use the `wait_on` package (Port sysutils/wait_on). This is quite easy to use, please see its manual page for detailed explanation and examples.


----------



## SirDice (Feb 5, 2019)

tuaris said:


> The idea here is that instead of polling for jobs (how it's currently done with the cron approach) this would be instantaneous.


Why don't you simply run the script when you detect the change? What's the point of detecting a change only to schedule a cronjob at a moments notice?


----------



## tuaris (Feb 5, 2019)

SirDice said:


> Why don't you simply run the script when you detect the change? What's the point of detecting a change only to schedule a cronjob at a moments notice?



I was considering that as an option, (bypassing `atrun` completely).  The main reason I want to use `atrun` is because it already implements the permissions mechanism for allowing who can run what commands.  I want to allow some root level tasks to be executed by specific non-root users (ie. restarting a service).



olli@ said:


> A much better way would be to not use polling at all, but get notified whenever the directory changes. The two advantages are: (a) you don't have to check the directory regularly, and (b) your script will react instantaneously, i.e. within fractions of a second. This can be done with FreeBSD's kqueue(2) feature. To use it from a shell script, you can use the `wait_on` package (Port sysutils/wait_on). This is quite easy to use, please see its manual page for detailed explanation and examples.



Thanks for that tip and the suggestion to use mtime.


----------



## tuaris (Feb 5, 2019)

I just realized that I can probably solve the permissions issue using the `sudo` command.


----------



## Eric A. Borisch (Feb 5, 2019)

See also sysutils/entr entr(1) for firing off a job on directory change.


----------

