-
-
Notifications
You must be signed in to change notification settings - Fork 501
/
dietpi-arr_to_RAM
328 lines (245 loc) · 10.3 KB
/
dietpi-arr_to_RAM
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
#!/bin/bash
{
#////////////////////////////////////
# DietPi Link Arr to RAM
#
#////////////////////////////////////
# Created by Daniel Knight / [email protected] / dietpi.com
#
#////////////////////////////////////
#
# Info:
# - Moves Sonarr, Radarr and Lidarr database files to RAM, leaving symlinks on disk
# - Reduces disk I/O and enhances database I/O performance
# - Creates a backup first and automatically restores from backup on next start in case of system crash
# NB: Not supported on Jessie: https://github.com/MichaIng/DietPi/issues/2689#issuecomment-487306241
#
readonly USAGE='
Usage: dietpi-arr_to_RAM <command> [<program>]
Available commands:
1 [<program>] Link (program) database(s) to RAM
2 [<program>] Update (program) database backup(s)
0 [<program>] Store (program) database(s) back to disk
enable Enable Link to RAM on boot
disable Disable Link to RAM on boot
Supported programs:
<empty> Apply to all supported and installed programs
sonarr Apply to Sonarr database only
radarr Apply to Radarr database only
lidarr Apply to Lidarr database only
' #////////////////////////////////////
# Import DietPi-Globals --------------------------------------------------------------
#. /boot/dietpi/func/dietpi-globals # Skip globals for faster execution on early boot stage
readonly PROGRAM_NAME='DietPi-Arr_to_RAM'
# Import DietPi-Globals --------------------------------------------------------------
# Grab input
INPUT=$1
INPUT_PROG=$2
EXIT_CODE=0
# Print output
# - In case of error, sets EXIT_CODE as well
unset -v error
Print(){
local message=$*
# shellcheck disable=SC2154
[[ $error ]] && { message="[ERROR] $message"; EXIT_CODE=$error; }
unset -v error
echo "$(date '+%Y-%m-%d %T') | $PROGRAM_NAME: $message"
}
# Check for required root or program specific permissions permissions
# - Handling systemd service always requires root permissions
# - Database backup updates can be done as program user when defined via $2
(( $UID )) && [[ $INPUT != 2 || $USER != "$INPUT_PROG" ]] && { error=1 Print 'This script must run as root user. Please use: "sudo"'; exit 1; }
# Boot service log file
FP_LOG='/var/tmp/dietpi/logs/dietpi-arr_to_RAM.log'
# Program database name array
declare -A aFILE=()
# - Sonarr
if [[ ${INPUT_PROG:-sonarr} == 'sonarr' ]]
then
# v3
if [[ -f '/mnt/dietpi_userdata/sonarr/sonarr.db' || -f '/mnt/dietpi_userdata/sonarr/sonarr.db.bak' ]]
then
aFILE[sonarr]='sonarr.db'
# v2
else
aFILE[sonarr]='nzbdrone.db'
fi
fi
# - Radarr
if [[ ${INPUT_PROG:-sonarr} == 'sonarr' ]]
then
# v3
if [[ -f '/mnt/dietpi_userdata/radarr/radarr.db' || -f '/mnt/dietpi_userdata/radarr/radarr.db.bak' ]]
then
aFILE[radarr]='radarr.db'
# v2
else
aFILE[radarr]='nzbdrone.db'
fi
fi
# - Lidarr
[[ ${INPUT_PROG:-lidarr} == 'lidarr' ]] && aFILE[lidarr]='lidarr.db'
# Check for valid input program
(( ${#aFILE[@]} )) || { error=1 Print "Invalid input program ($INPUT_PROG). Aborting...
$USAGE"; exit 1; }
FP_DISK=
FP_RAM=
Link_To_Ram(){
Print "Linking $FP_DISK to RAM ($FP_RAM)..."
# Remove orphaned symlinks before creating backup, else sqlite3 will fail to open the database
[[ -L "$FP_DISK-shm" && ! -f "$FP_DISK-shm" ]] && rm "$FP_DISK-shm"
[[ -L "$FP_DISK-wal" && ! -f "$FP_DISK-wal" ]] && rm "$FP_DISK-wal"
# Create a backup first, which includes -shm and -wal, then copy that to RAM. If it fails, skip that program.
sqlite3 "$FP_DISK" ".save ${FP_DISK}.bak" || { error=$? Print "Creating ${i^} database backup failed. Skipping this program..."; return 1; }
chown "$i" "${FP_DISK}.bak" || { error=$? Print "Setting ${i^} database backup ownership failed. Skipping this program..."; return 1; }
cp -aL --no-preserve=timestamps "${FP_DISK}.bak" "$FP_RAM" || { error=$? Print "Copying ${i^} database to RAM failed. Skipping this program..."; return 1; }
# Create all symlinks and chown them in case created by root but program user wants to store back to disk
local j
for j in '' '-shm' '-wal'
do
ln -sf "$FP_RAM$j" "$FP_DISK$j" || EXIT_CODE=$?
chown -h "$i" "$FP_DISK$j" || EXIT_CODE=$?
done
Print "Linked ${i^} database to RAM."
}
Toggle_Link_To_Ram(){
local astart_services=()
# Loop through programs
local i
for i in "${!aFILE[@]}"
do
FP_DISK="/mnt/dietpi_userdata/$i"
FP_RAM="/tmp/${i}_db_link"
# Skip non-installed program
[[ -d $FP_DISK ]] || continue
Print "${i^} detected"
# Create update backup script
if [[ ! -f $FP_DISK/dietpi-arr_to_RAM.sh ]]
then
Print "Creating $FP_DISK/dietpi-arr_to_RAM.sh to be used as ${i^} custom script"
echo -e '#!/bin/dash\n/boot/dietpi/misc/dietpi-arr_to_RAN 2 '"$i" > "$FP_DISK/dietpi-arr_to_RAM.sh" || { error=$? Print "Creating $FP_DISK/dietpi-arr_to_RAM.sh failed."; }
chmod +x "$FP_DISK/dietpi-arr_to_RAM.sh" || { error=$? Print "Applying $FP_DISK/dietpi-arr_to_RAM.sh execute permissions failed."; }
chown "$i:dietpi" "$FP_DISK/dietpi-arr_to_RAM.sh" || { error=$? Print "Applying $FP_DISK/dietpi-arr_to_RAM.sh ownership failed."; }
fi
# Update backup
if (( $INPUT == 2 )); then
if [[ -f $FP_RAM/${aFILE[$i]} ]]; then
Print "Updating ${i^} database backup..."
sqlite3 "$FP_RAM/${aFILE[$i]}" ".save $FP_DISK/${aFILE[$i]}.bak" || { error=$? Print "Updating ${i^} database backup failed."; continue; }
Print "Updated ${i^} database backup."
else
Print "${i^} database is not in RAM. Skipping this program..."
fi
continue
fi
# If active, stop program before handling database and restart afterwards
if pgrep -f "$i" > /dev/null; then
Print "Stopping ${i^} service..."
astart_services+=("$i")
systemctl stop "$i" || { error=$? Print "Stopping ${i^} service failed. Skipping this program..."; continue; }
fi
# Link to RAM + backup
if (( $INPUT == 1 )); then
Print "Linking ${i^} database to RAM..."
# - Pre-create RAM dir
[[ -d $FP_RAM ]] || mkdir -p "$FP_RAM" || { error=$? Print "Pre-creating RAM directory for ${i^} failed ($FP_RAM). Skipping this program..."; continue; }
# - chown dir in case created by root but program user wants to store back to disk
chown "$i" "$FP_RAM" || EXIT_CODE=$?
# Process database file
FP_DISK+="/${aFILE[$i]}"
FP_RAM+="/${aFILE[$i]}"
# Source exists
local fp_target
if fp_target=$(readlink -e "$FP_DISK"); then
# Link to target exists, should only happen when running the script two times in same session
if [[ $fp_target == "$FP_RAM" ]]; then
Print "$FP_DISK already linked to RAM ($FP_RAM). Skipping this program..."
# Source exist, but is not linked to RAM yet
else
Link_To_Ram || continue
fi
# Source does not exist or is orphaned link, but backup exists, which is expected after system crash
elif [[ -f ${FP_DISK}.bak ]]; then
Print "$FP_DISK not found. Recovering from backup first (${FP_DISK}.bak)..."
# Remove possible orphaned symlink
[[ ! -L $FP_DISK ]] || rm "$FP_DISK" || { error=$? Print "Removing orphaned database symlink failed ($FP_DISK). Skipping this program..."; continue; }
# Recover from backup
mv "${FP_DISK}.bak" "$FP_DISK" || { error=$? Print "Recovering database from backup failed (${FP_DISK}.bak). Skipping this program..."; continue; }
Link_To_Ram || continue
else
Print "$FP_DISK not found. Skipping this program..."
fi
# Store back to disk
elif (( $INPUT == 0 )); then
if [[ -d $FP_RAM ]]; then
Print "Storing database from RAM ($FP_RAM) back to disk ($FP_DISK)..."
# "-u" will only copy newer files, thus actually used by program.
# "--remove-destination" will remove expected existing symlinks.
cp -au --remove-destination "$FP_RAM/." "$FP_DISK" || { error=$? Print "Storing ${i^} database from RAM back to disk failed."; continue; }
rm -R "$FP_RAM"
# Remove orphaned symlinks, possible when -shm and -wal do currently not exist
local j
for j in "$FP_DISK/${aFILE[$i]}"{,-shm,-wal}
do
[[ ! -L $j || -f $j ]] || rm "$j" || EXIT_CODE=$?
done
Print "Stored ${i^} database from RAM back to disk."
else
Print "${i^} database is not in RAM. Skipping this program..."
fi
fi
done
# Failsafe: When restoring to disk, "sync" now to prevent async issues!
(( $INPUT == 0 )) && sync
# Start programs we stopped before
# - NB: Due to Before=, *arr.service waits for dietpi-arr_to_RAM.service to finish, timing it out.
# - "--no-block" allows dietpi-arr_to_RAM.service to only enqueue *arr.service starts and finish, to allow them starting afterwards.
if [[ ${astart_services[0]} ]]; then
Print "Enqueuing ${astart_services[*]} service start(s)..."
systemctl --no-block start "${astart_services[@]}"
fi
}
Enable_On_Boot(){
cat << _EOF_ > /etc/systemd/system/dietpi-arr_to_RAM.service
[Unit]
Description=DietPi-Arr_to_RAM
Requisite=tmp.mount
After=tmp.mount
Before=dietpi-preboot.service sonarr.service radarr.service lidarr.service
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=-/bin/dash -c '/boot/dietpi/misc/dietpi-arr_to_RAM 1 2>&1 >> $FP_LOG'
ExecStop=/bin/dash -c '/boot/dietpi/misc/dietpi-arr_to_RAM 0 2>&1 > $FP_LOG'
[Install]
WantedBy=multi-user.target
_EOF_
systemctl daemon-reload
systemctl enable --now dietpi-arr_to_RAM || EXIT_CODE=$?
}
Disable_On_Boot(){
if [[ -f '/etc/systemd/system/dietpi-arr_to_RAM.service' ]]; then
systemctl disable --now dietpi-arr_to_RAM || EXIT_CODE=$?
rm /etc/systemd/system/dietpi-arr_to_RAM.service || EXIT_CODE=$?
fi
}
#/////////////////////////////////////////////////////////////////////////////////////
# Main Loop
#/////////////////////////////////////////////////////////////////////////////////////
# Toggle Link to RAM
if [[ $INPUT == [012] ]]; then
Toggle_Link_To_Ram
(( $EXIT_CODE )) && Print '[ERROR] An issue has occurred. Please check the above output for details.'
# Enable/Disable Link to RAM on boot
elif [[ $INPUT == 'enable' || $INPUT == 'disable' ]]; then
"${INPUT^}_On_Boot"
(( $EXIT_CODE )) && Print "[ERROR] An issue has occurred. Please check the log for details: $FP_LOG"
else
error=1 Print "Invalid input command (${INPUT:-<empty>}). Aborting...
$USAGE"
fi
#-----------------------------------------------------------------------------------
exit $EXIT_CODE
#-----------------------------------------------------------------------------------
}