Skip to content

Commit 55a3dea

Browse files
committed
Add JavaScript patterns for progress indicators and modal dialogs
- Introduced loading spinner and progress bar examples. - Added functions for handling long-running tasks with progress updates. - Included usage of SweetAlert for modal dialogs with various types (alerts, confirmations, inputs). - Provided custom modal implementation with HTML, CSS, and JavaScript. - Updated best practices for user feedback and preventing double submissions. Create documentation for plugin distribution and Community Applications - Added a new section on distribution and publishing of Unraid plugins. - Documented the process for getting listed in Community Applications. - Included requirements for listing, XML template structure, and submission process. - Outlined best practices for plugin description writing and version compatibility. Establish comprehensive reference for $var array - Created a detailed reference for the $var array, including system state and configuration variables. - Documented access methods, complete property reference, and common usage patterns. - Provided examples for checking array state, server information, and CSRF token usage. - Included notes on related state files and disk/share information access.
1 parent c5d087b commit 55a3dea

14 files changed

Lines changed: 4220 additions & 346 deletions

docs/advanced/array-disk-access.md

Lines changed: 345 additions & 74 deletions
Large diffs are not rendered by default.

docs/core/cron-jobs.md

Lines changed: 269 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,21 @@ nav_order: 7
77

88
# Cron Jobs
99

10-
{: .warning }
11-
> This page is a stub. [Help us expand it!](https://github.com/mstrhakr/unraid-plugin-docs/blob/main/CONTRIBUTING.md)
12-
1310
## Overview
1411

15-
Plugins can add scheduled tasks using cron. Unraid uses the standard cron daemon with files in `/etc/cron.d/`.
12+
Plugins can add scheduled tasks using cron. Unraid uses the standard cron daemon with files in `/etc/cron.d/`. Additionally, Unraid provides a built-in **Dynamix Scheduler** that offers a GUI-based interface for managing scheduled tasks.
13+
14+
## Methods for Scheduling Tasks
1615

17-
## Adding a Cron Job
16+
There are three primary ways to schedule tasks in Unraid:
17+
18+
| Method | Best For | Persistence |
19+
|--------|----------|-------------|
20+
| `/etc/cron.d/` files | System-level tasks | Recreate on boot |
21+
| Dynamix Scheduler | User-configurable tasks | Automatic via GUI |
22+
| User Scripts plugin | User-defined scripts | Via plugin settings |
23+
24+
## Adding a Cron Job via /etc/cron.d/
1825

1926
Cron files should be installed to `/etc/cron.d/` since this directory is in RAM and recreated on boot.
2027

@@ -47,6 +54,132 @@ case "$1" in
4754
esac
4855
```
4956

57+
## Dynamix Scheduler Integration
58+
59+
The Dynamix Scheduler provides a GUI for scheduling tasks at Settings → Scheduler. Plugins can integrate with this system to let users configure schedules without editing cron files manually.
60+
61+
### Scheduler Configuration File
62+
63+
The Dynamix Scheduler reads from `/boot/config/plugins/dynamix/dynamix.cfg`:
64+
65+
```ini
66+
# Example scheduler entries in dynamix.cfg
67+
parity="0|0|*|*|*" # Parity check schedule
68+
mover="0|3|*|*|*" # Mover schedule
69+
```
70+
71+
### Adding Plugin Tasks to the Scheduler
72+
73+
To integrate with the Dynamix Scheduler, your plugin can add entries to the scheduler page:
74+
75+
```php
76+
<?
77+
// In your settings page, provide scheduler controls
78+
$cfg = parse_plugin_cfg("yourplugin");
79+
80+
// Default schedule: disabled
81+
$schedule = $cfg['schedule'] ?? '';
82+
83+
// Parse cron schedule into components
84+
if ($schedule) {
85+
list($min, $hour, $dom, $mon, $dow) = explode(' ', $schedule);
86+
} else {
87+
$min = $hour = $dom = $mon = $dow = '*';
88+
}
89+
?>
90+
```
91+
92+
### Scheduler UI Controls
93+
94+
Use the standard Dynamix scheduler dropdown pattern:
95+
96+
```html
97+
<dl>
98+
<dt>Schedule:</dt>
99+
<dd>
100+
<select name="schedule_type" onchange="toggleSchedule(this.value)">
101+
<option value="disabled" <?=($cfg['schedule_type']=='disabled')?'selected':''?>>Disabled</option>
102+
<option value="hourly" <?=($cfg['schedule_type']=='hourly')?'selected':''?>>Hourly</option>
103+
<option value="daily" <?=($cfg['schedule_type']=='daily')?'selected':''?>>Daily</option>
104+
<option value="weekly" <?=($cfg['schedule_type']=='weekly')?'selected':''?>>Weekly</option>
105+
<option value="monthly" <?=($cfg['schedule_type']=='monthly')?'selected':''?>>Monthly</option>
106+
<option value="custom" <?=($cfg['schedule_type']=='custom')?'selected':''?>>Custom</option>
107+
</select>
108+
</dd>
109+
</dl>
110+
111+
<div id="schedule_time" style="display:none">
112+
<dl>
113+
<dt>Time:</dt>
114+
<dd>
115+
<select name="schedule_hour">
116+
<? for ($h = 0; $h < 24; $h++): ?>
117+
<option value="<?=$h?>" <?=($cfg['schedule_hour']==$h)?'selected':''?>>
118+
<?=sprintf("%02d:00", $h)?>
119+
</option>
120+
<? endfor; ?>
121+
</select>
122+
</dd>
123+
</dl>
124+
</div>
125+
126+
<script>
127+
function toggleSchedule(type) {
128+
var timeDiv = document.getElementById('schedule_time');
129+
timeDiv.style.display = (type !== 'disabled') ? 'block' : 'none';
130+
}
131+
// Initialize on load
132+
toggleSchedule(document.querySelector('[name="schedule_type"]').value);
133+
</script>
134+
```
135+
136+
### Writing the Cron Entry from Settings
137+
138+
When the user saves settings, write the cron file:
139+
140+
```php
141+
<?
142+
// In your update.php or settings handler
143+
function updateCronSchedule($cfg) {
144+
$cronFile = "/etc/cron.d/yourplugin";
145+
146+
if ($cfg['schedule_type'] === 'disabled') {
147+
// Remove cron job
148+
@unlink($cronFile);
149+
return;
150+
}
151+
152+
// Build cron schedule based on type
153+
switch ($cfg['schedule_type']) {
154+
case 'hourly':
155+
$schedule = "0 * * * *";
156+
break;
157+
case 'daily':
158+
$hour = intval($cfg['schedule_hour']);
159+
$schedule = "0 $hour * * *";
160+
break;
161+
case 'weekly':
162+
$hour = intval($cfg['schedule_hour']);
163+
$dow = intval($cfg['schedule_dow']);
164+
$schedule = "0 $hour * * $dow";
165+
break;
166+
case 'monthly':
167+
$hour = intval($cfg['schedule_hour']);
168+
$dom = intval($cfg['schedule_dom']);
169+
$schedule = "0 $hour $dom * *";
170+
break;
171+
case 'custom':
172+
$schedule = $cfg['schedule_custom'];
173+
break;
174+
}
175+
176+
// Write cron file
177+
$cronEntry = "$schedule root /usr/local/emhttp/plugins/yourplugin/scripts/scheduled_task.sh >/dev/null 2>&1\n";
178+
file_put_contents($cronFile, $cronEntry);
179+
}
180+
?>
181+
```
182+
50183
## Cron Syntax Reference
51184

52185
```
@@ -63,11 +196,14 @@ esac
63196

64197
| Pattern | Description |
65198
|---------|-------------|
66-
| `0 * * * *` | Every hour |
199+
| `0 * * * *` | Every hour (on the hour) |
67200
| `*/5 * * * *` | Every 5 minutes |
68201
| `0 0 * * *` | Daily at midnight |
202+
| `0 3 * * *` | Daily at 3:00 AM |
69203
| `0 3 * * 0` | Weekly on Sunday at 3 AM |
70-
| `0 0 1 * *` | Monthly on the 1st |
204+
| `0 0 1 * *` | Monthly on the 1st at midnight |
205+
| `0 4 * * 1-5` | Weekdays at 4:00 AM |
206+
| `*/15 9-17 * * *` | Every 15 min during 9 AM - 5 PM |
71207

72208
## User Context
73209

@@ -77,6 +213,9 @@ Always specify the user (typically `root`) in `/etc/cron.d/` files:
77213
* * * * * root /path/to/script.sh
78214
```
79215

216+
{: .warning }
217+
> Unlike user crontabs, `/etc/cron.d/` files require the username field. Omitting it will cause your job to fail silently.
218+
80219
## Logging Output
81220

82221
```bash
@@ -88,6 +227,9 @@ Always specify the user (typically `root`) in `/etc/cron.d/` files:
88227

89228
# Log to syslog
90229
0 * * * * root /script.sh 2>&1 | logger -t yourplugin
230+
231+
# Log with timestamp to file
232+
0 * * * * root /script.sh >> /var/log/yourplugin.log 2>&1 && echo "$(date): Task completed" >> /var/log/yourplugin.log
91233
```
92234

93235
## Persistence
@@ -96,6 +238,126 @@ Remember that `/etc/cron.d/` is in RAM. Your cron jobs must be recreated:
96238
- On boot (via PLG FILE element or rc.d script)
97239
- After array start if they depend on array
98240

241+
### Ensuring Cron Jobs Persist
242+
243+
**Option 1: PLG FILE element** (recommended for static schedules)
244+
```xml
245+
<FILE Name="/etc/cron.d/yourplugin">
246+
<INLINE>
247+
0 * * * * root /usr/local/emhttp/plugins/yourplugin/scripts/task.sh &gt;/dev/null 2&gt;&amp;1
248+
</INLINE>
249+
</FILE>
250+
```
251+
252+
**Option 2: rc.d script** (recommended for configurable schedules)
253+
```bash
254+
#!/bin/bash
255+
# /etc/rc.d/rc.yourplugin
256+
PLUGIN="yourplugin"
257+
CRONFILE="/etc/cron.d/$PLUGIN"
258+
CONFIG="/boot/config/plugins/$PLUGIN/$PLUGIN.cfg"
259+
260+
case "$1" in
261+
'start')
262+
# Read schedule from config and write cron entry
263+
if [ -f "$CONFIG" ]; then
264+
source "$CONFIG"
265+
if [ -n "$SCHEDULE" ] && [ "$SCHEDULE" != "disabled" ]; then
266+
echo "$SCHEDULE root /usr/local/emhttp/plugins/$PLUGIN/scripts/task.sh >/dev/null 2>&1" > "$CRONFILE"
267+
fi
268+
fi
269+
;;
270+
'stop')
271+
rm -f "$CRONFILE"
272+
;;
273+
'restart')
274+
$0 stop
275+
$0 start
276+
;;
277+
esac
278+
```
279+
280+
**Option 3: Event script** (for array-dependent tasks)
281+
```bash
282+
#!/bin/bash
283+
# /usr/local/emhttp/plugins/yourplugin/event/started_array
284+
echo "0 * * * * root /usr/local/emhttp/plugins/yourplugin/scripts/array_task.sh >/dev/null 2>&1" > /etc/cron.d/yourplugin_array
285+
```
286+
287+
## Running Scripts at Specific Array States
288+
289+
Sometimes you want cron jobs only when the array is running:
290+
291+
```bash
292+
#!/bin/bash
293+
# /usr/local/emhttp/plugins/yourplugin/scripts/conditional_task.sh
294+
295+
# Check if array is started
296+
if [ ! -d /mnt/user ]; then
297+
logger -t "yourplugin" "Array not started, skipping task"
298+
exit 0
299+
fi
300+
301+
# Your task here
302+
logger -t "yourplugin" "Running scheduled task"
303+
```
304+
305+
## Debugging Cron Jobs
306+
307+
### Check if cron is running
308+
```bash
309+
ps aux | grep cron
310+
```
311+
312+
### View cron execution in syslog
313+
```bash
314+
tail -f /var/log/syslog | grep CRON
315+
```
316+
317+
### Test your script manually
318+
```bash
319+
/usr/local/emhttp/plugins/yourplugin/scripts/task.sh
320+
echo $? # Check exit code
321+
```
322+
323+
### Verify cron file syntax
324+
```bash
325+
cat /etc/cron.d/yourplugin
326+
# Ensure: schedule user command format
327+
# Ensure: file has newline at end
328+
```
329+
330+
## Best Practices
331+
332+
1. **Always escape XML entities** in PLG files (`>` becomes `&gt;`, `&` becomes `&amp;`)
333+
2. **Redirect output** to avoid email spam from cron
334+
3. **Add logging** for debugging scheduled tasks
335+
4. **Check array state** before accessing array paths
336+
5. **Use locking** to prevent overlapping runs of long tasks:
337+
338+
```bash
339+
#!/bin/bash
340+
LOCKFILE="/var/run/yourplugin_task.lock"
341+
342+
# Check for existing lock
343+
if [ -f "$LOCKFILE" ]; then
344+
# Check if process is actually running
345+
PID=$(cat "$LOCKFILE")
346+
if kill -0 "$PID" 2>/dev/null; then
347+
logger -t "yourplugin" "Task already running (PID $PID)"
348+
exit 0
349+
fi
350+
fi
351+
352+
# Create lock
353+
echo $$ > "$LOCKFILE"
354+
trap "rm -f $LOCKFILE" EXIT
355+
356+
# Your task here
357+
sleep 60
358+
logger -t "yourplugin" "Task completed"
359+
```
360+
99361
## Related Topics
100362

101363
- [PLG File Reference]({% link docs/plg-file.md %})

0 commit comments

Comments
 (0)