├── .gitignore ├── FanControl.tool ├── LICENSE ├── README.md ├── ipfw.rules ├── jls.tool ├── pia-port-forward.sh ├── search.cmd ├── suite-definition.xml └── tlerActiveation.tool /.gitignore: -------------------------------------------------------------------------------- 1 | FanConfig 2 | jls.cfg 3 | -------------------------------------------------------------------------------- /FanControl.tool: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 4 | # Config 5 | # 6 | # shellcheck disable=SC2236 7 | # 8 | 9 | # Write out a default config file 10 | function fcConfig { 11 | tee > "${configFile}" <<"EOF" 12 | # FanControl config file 13 | 14 | # Set this to 0 to enable 15 | defaultFile="1" 16 | 17 | # Fan settings 18 | autoFanDuty="0" # Value that sets fan to auto based on CPU temp. 19 | minFanDuty="30" # Minimum effective value to set duty level to. 20 | maxFanDuty="100" # Maxim value to set duty level to. 21 | difFanDuty="10" # The difference maintained between intake and exhaust fans 22 | mixedFans="0" # Set to one if the same fans are responsible for both hdd and ssd 23 | 24 | 25 | # Temperatures in Celsius 26 | targetHDriveTemp="31" # The temperature that we try to maintain for HDs. 27 | targetSSDriveTemp="36" # The temperature that we try to maintain for SSDs. 28 | maxDriveTemp="39" # Do not let drives get hotter than this. 29 | ambTempVariance="1" # How many degrees the ambient temperature may effect the target 30 | 31 | # CPU settings 32 | # Currently unused 33 | # targetCPUTemp="35" # The temperature that we try to maintain. 34 | # maxCPUTemp="55" # Do not let the CPU get hotter than this. 35 | # cpuCheckTempInterval="2" # In seconds. 36 | 37 | # HBA Settings 38 | targetHBATemp="41" # The temperature that we try to maintain. 39 | maxHBATemp="55" # Do not let the HBA get hotter than this. 40 | 41 | 42 | # PID Controls 43 | Kp="4" # Proportional tunable constant 44 | Ki="0.040" # Integral tunable constant 45 | Kd="16" # Derivative tunable constant 46 | 47 | # Time interval to check disk temps in mins 48 | diskCheckTempInterval="2" 49 | 50 | 51 | # List of HDs 52 | hdName=( 53 | da0 54 | da1 55 | da2 56 | da3 57 | da4 58 | da5 59 | da6 60 | da7 61 | ) 62 | 63 | # List of SSDs 64 | ssdName=( 65 | ada0 66 | ada1 67 | ada2 68 | ada3 69 | ada4 70 | ada5 71 | ) 72 | 73 | # List of HBAs 74 | # See https://gist.github.com/dak180/cd44e9957e1c4180e7eb6eb000716ee2 75 | lsi_temp="/mnt/jails/scripts/lsi_temp" # path to compiled program ^ 76 | 77 | # sesutil map -u /dev/sesX will tell you if there are temp sensors in your 78 | # backplane or expander that you can read. 79 | 80 | hbaName=( 81 | mpr0 82 | ses0 83 | ) 84 | 85 | 86 | # Everything below this line is MB specific. 87 | # These examples are for an ASRockRack E3C236D4U. 88 | 89 | # 90 | # Currently unused 91 | # 92 | # Fan sensor name prefixes; numbers will be added 93 | # senNamePrefixCPU_FAN="CPU_FAN" 94 | # senNamePrefixFRNT_FAN="FRNT_FAN" 95 | # senNamePrefixREAR_FAN="REAR_FAN" 96 | 97 | 98 | # Temp sensors 99 | # The names used by ipmi. 100 | # cpuTempSens[0]="CPU1 Temp" # CPU temp Currently unused 101 | hbaTempSens[0]="TR1 Temp" # HBA temp 102 | ambTempSens[0]="MB Temp" # Ambient temp 103 | ambTempSens[1]="Card Side Temp" # Ambient temp 104 | 105 | if [ ! "${#hbaTempSens[@]}" = "0" ]; then 106 | hbaName+=("${hbaTempSens[@]}") 107 | fi 108 | 109 | 110 | # IPMI Fan Commands 111 | # 112 | # The script curently tracks five different types of fan values: 113 | # CPU_FAN is used for fans that directly cools the cpu. 114 | # HBA_FAN is used for fans that directly cools a HBA card. 115 | # FRNT_FAN is used for intake fans. 116 | # REAR_FAN is used for exhaust fans. 117 | # NIL_FAN is used for space holder values that are not fans. 118 | # 119 | # Make sure that you set values here corectly for your board. 120 | 121 | # The following is a template for the ASRockRack E3C236D4U board other ASRockRack 122 | # boards may use a similar call. 123 | # ipmitool raw 0x3a 0x01 ${CPU_FAN1} ${Reserved} ${REAR_FAN1} ${REAR_FAN2} ${FRNT_FAN1} ${FRNT_FAN2} ${FRNT_FAN3} ${Reserved} 124 | # The command to set the desired fan duty levels. 125 | function ipmiWrite { 126 | if ! ipmitool raw 0x3a 0x01 "${CPU_FAN[0]}" "${NIL_FAN[0]}" "${REAR_FAN[0]}" "${NIL_FAN[1]}" "${FRNT_FAN[0]}" "${SSD_FAN[0]}" "${HBA_FAN[0]}" "${NIL_FAN[2]}"; then 127 | return ${?} 128 | fi 129 | } 130 | 131 | # A function to read the current fan duty levels. 132 | # It converts hex values to decimal and seperates them by type. 133 | function ipmiRead { 134 | local rawFan 135 | local rawFanArray 136 | 137 | # Command to get current fan duty levels as a space separated list. 138 | rawFan="$(ipmitool raw 0x3a 0x02 | sed -e 's:^ *::')" 139 | 140 | read -ra rawFanArray <<< "${rawFan}" 141 | 142 | # Assign named values from the raw array. 143 | CPU_FAN[0]="$(hexConv "${rawFanArray[0]}")" 144 | NIL_FAN[0]="$(hexConv "${rawFanArray[1]}")" 145 | REAR_FAN[0]="$(hexConv "${rawFanArray[2]}")" 146 | NIL_FAN[1]="$(hexConv "${rawFanArray[3]}")" 147 | FRNT_FAN[0]="$(hexConv "${rawFanArray[4]}")" 148 | FRNT_FAN[1]="$(hexConv "${rawFanArray[5]}")" 149 | FRNT_FAN[2]="$(hexConv "${rawFanArray[6]}")" 150 | NIL_FAN[2]="$(hexConv "${rawFanArray[7]}")" 151 | 152 | # Remap from MB supplied names. 153 | HBA_FAN[0]="${FRNT_FAN[2]}" 154 | SSD_FAN[0]="${FRNT_FAN[1]}" 155 | } 156 | 157 | EOF 158 | exit 0 159 | } 160 | 161 | # 162 | # Functions Start Here 163 | # 164 | 165 | # ipmi sensor command 166 | function ipmiSens { 167 | local sensName="${1}" 168 | ipmitool -c sdr get "${sensName}" | cut -d ',' -f 2 169 | } 170 | 171 | # Convert Hex to decimal 172 | function hexConv { 173 | local hexIn="${1}" 174 | echo "$((0x${hexIn}))" 175 | } 176 | 177 | # Round to nearest whole number 178 | function roundR { 179 | bc <<< "scale=0;(${1} + 0.5) / 1" 180 | } 181 | 182 | 183 | # Get average or high CPU temperature. 184 | function cpuTemp { 185 | local numberCPU="${1}" 186 | local numberCPUArray 187 | local coreCPU 188 | local cpuTempCur 189 | local cpuTempAv="0" 190 | local cpuTempMx="0" 191 | read -ra numberCPUArray <<< "$(seq "0 ${numberCPU}")" 192 | 193 | for coreCPU in "${numberCPUArray[@]}"; do 194 | cpuTempCur="$(sysctl -n "dev.cpu.${coreCPU}.temperature" | sed -e 's:C::')" 195 | 196 | # Start adding temps for an average. 197 | cpuTempAv="$(bc <<< "scale=3;${cpuTempCur} + ${cpuTempAv}")" 198 | 199 | # Keep track of the highest current temp 200 | if [ "$(roundR "${cpuTempMx}")" -gt "$(roundR "${cpuTempCur}")" ]; then 201 | true 202 | else 203 | cpuTempMx="${cpuTempCur}" 204 | fi 205 | done 206 | # Divide by number of CPUs for average. 207 | hdTempAv="$(bc <<< "scale=3;${cpuTempAv} / ${#numberCPUArray[@]}")" 208 | 209 | # If the hottest CPU matches/exceeds the max temp use that 210 | # instead of the average. 211 | if [ "${cpuTempMx}" -gt "${cpuTempCur}" ]; then 212 | true 213 | else 214 | cpuTempMx="${cpuTempCur}" 215 | fi 216 | } 217 | 218 | 219 | # Get set point temp 220 | function targetTemp { 221 | local ambTemIn 222 | local ambTemOut="0" 223 | local ambTemCur 224 | local targetDiskTemp="${1}" 225 | 226 | for ambTemIn in "${ambTempSens[@]}"; do 227 | # Get the current ambent temp readings. 228 | ambTemCur="$(ipmiSens "${ambTemIn}")" 229 | # Start adding temps for an average. 230 | ambTemOut="$(bc <<< "scale=0;${ambTemOut} + ${ambTemCur}")" 231 | done 232 | # Divide by number of sensors for average. 233 | ambTemOut="$(bc <<< "scale=3;${ambTemOut} / ${#ambTempSens[@]}")" 234 | ambTemRoun="$(roundR "${ambTemOut}")" 235 | ambTemComp="$(bc <<< "scale=0;${ambTemOut} / 1")" 236 | 237 | # Alow the target temp to vary by $ambTempVariance degrees based on 238 | # a difference between ambent internal temp and $targetDiskTemp. 239 | if [ "${ambTemComp}" = "${targetDiskTemp}" ]; then 240 | echo "${ambTemOut}" 241 | else 242 | if [ "${ambTemRoun}" -gt "${targetDiskTemp}" ]; then 243 | if [ "${ambTemRoun}" -gt "$(bc <<< "scale=0;${targetDiskTemp} + ${ambTempVariance}")" ]; then 244 | bc <<< "scale=3;${targetDiskTemp} + ${ambTempVariance}" 245 | return 0 246 | fi 247 | elif [ "${targetDiskTemp}" -gt "${ambTemComp}" ]; then 248 | if [ "$(bc <<< "scale=0;${targetDiskTemp} - ${ambTempVariance}")" -gt "${ambTemComp}" ]; then 249 | bc <<< "scale=3;${targetDiskTemp} - ${ambTempVariance}" 250 | return 0 251 | fi 252 | fi 253 | echo "${ambTemOut}" 254 | fi 255 | } 256 | 257 | 258 | # Get average or high HD temperature. 259 | function hdTemp { 260 | local hdNum 261 | local hdTempCur 262 | local hdTempAv="0" 263 | local hdTempMx="0" 264 | local hdComp="0" 265 | 266 | for hdNum in "${hdName[@]}"; do 267 | # Get the temp for the current drive. 268 | hdTempCur="$(smartctl -aj "/dev/${hdNum}" | jq -Mre '.temperature.current | values')" 269 | # Start adding temps for an average. 270 | hdTempAv="$(( hdTempAv + hdTempCur ))" 271 | 272 | # Track non responsive drives 273 | if [ -z "${hdTempCur}" ]; then 274 | hdComp="$(( hdComp + 1 ))" 275 | fi 276 | 277 | # Keep track of the highest current temp 278 | if [ "${hdTempMx}" -gt "${hdTempCur}" ]; then 279 | true 280 | else 281 | hdTempMx="${hdTempCur}" 282 | fi 283 | done 284 | # Divide by number of drives for average. 285 | hdTempAv="$(bc <<< "scale=3;${hdTempAv} / (${#hdName[@]} - ${hdComp})")" 286 | 287 | # If the hottest drive matches/exceeds the max temp use that instead 288 | # of the average. 289 | if [ "${hdTempMx}" -ge "${maxDriveTemp}" ]; then 290 | echo "${hdTempMx}" 291 | else 292 | echo "${hdTempAv}" 293 | fi 294 | } 295 | 296 | 297 | # Get average or high ssd temperature. 298 | function ssdTemp { 299 | local ssdNum 300 | local ssdTempCur 301 | local ssdTempAv="0" 302 | local ssdTempMx="0" 303 | local ssdTempMn 304 | local ssdComp="0" 305 | 306 | for ssdNum in "${ssdName[@]}"; do 307 | # Get the temp for the current drive. 308 | ssdTempCur="$(smartctl -aj "/dev/${ssdNum}" | jq -Mre '.temperature.current | values')" 309 | # Start adding temps for an average. 310 | ssdTempAv="$(( ssdTempAv + ssdTempCur ))" 311 | 312 | # Track non responsive drives 313 | if [ -z "${ssdTempCur}" ]; then 314 | ssdComp="$(( ssdComp + 1 ))" 315 | fi 316 | 317 | # Keep track of the lowest current temp 318 | if [ "${ssdTempMn:="${ssdTempCur}"}" -lt "${ssdTempCur}" ]; then 319 | true 320 | else 321 | ssdTempMn="${ssdTempCur}" 322 | fi 323 | 324 | # Keep track of the highest current temp 325 | if [ "${ssdTempMx}" -gt "${ssdTempCur}" ]; then 326 | true 327 | else 328 | ssdTempMx="${ssdTempCur}" 329 | fi 330 | done 331 | 332 | if [ "$((ssdTempMx - ssdTempMn))" -ge "10" ]; then 333 | ssdComp="$(( ssdComp + 1 ))" 334 | fi 335 | 336 | # Divide by number of drives for average. 337 | ssdTempAv="$(bc <<< "scale=3;${ssdTempAv} / (${#ssdName[@]} - ${ssdComp})")" 338 | 339 | # If the hottest drive matches/exceeds the max temp use that instead 340 | # of the average. 341 | if [ "${ssdTempMx}" -ge "${maxDriveTemp}" ]; then 342 | echo "${ssdTempMx}" 343 | else 344 | echo "${ssdTempAv}" 345 | fi 346 | } 347 | 348 | 349 | # Get average or high hba temperature. 350 | function hbaTemp { 351 | local hbaNum 352 | local hbaTempCur 353 | local hbaTempAv="0" 354 | local hbaTempMx="0" 355 | 356 | for hbaNum in "${hbaName[@]}"; do 357 | # Get the temp for the current hba. 358 | if [ -c "/dev/${hbaNum}" ]; then 359 | if echo "${hbaNum}" | grep -q "mpr"; then 360 | # See https://gist.github.com/dak180/cd44e9957e1c4180e7eb6eb000716ee2 361 | hbaTempCur="$(${lsi_temp} "/dev/${hbaNum}" | grep 'IOC' | cut -d ' ' -f 3)" 362 | elif echo "${hbaNum}" | grep -q "ses"; then 363 | hbaTempCur="$(sesutil map -u "/dev/${hbaNum}" --libxo:J | jq --arg hbaNum "${hbaNum}" -Mre '.sesutil.enclosures[] | select(.enc == $hbaNum) | .elements[] | select((.type == "Temperature Sensor") and (.status == "OK")) | .extra_status.temperature | values')" 364 | fi 365 | else 366 | hbaTempCur="$(ipmiSens "${hbaNum}")" 367 | fi 368 | # Start adding temps for an average. 369 | hbaTempAv="$(( hbaTempAv + hbaTempCur ))" 370 | 371 | # Keep track of the highest current temp 372 | if [ "${hbaTempMx}" -gt "${hbaTempCur}" ]; then 373 | true 374 | else 375 | hbaTempMx="${hbaTempCur}" 376 | fi 377 | done 378 | # Divide by number of hba for average. 379 | hbaTempAv="$(bc <<< "scale=3;${hbaTempAv} / ${#hbaName[@]}")" 380 | 381 | # If the hottest drive matches/exceeds the max temp use that instead 382 | # of the average. 383 | if [ "${hbaTempMx}" -ge "${maxHBATemp}" ]; then 384 | echo "${hbaTempMx}" 385 | else 386 | echo "${hbaTempAv}" 387 | fi 388 | } 389 | 390 | 391 | # Print temp info 392 | function infoTemps { 393 | local hdNum 394 | local hdTempCur 395 | local hdTempAv="0" 396 | local hdComp="0" 397 | local ssdNum 398 | local ssdTempCur 399 | local ssdTempAv="0" 400 | local ssdTempMn 401 | local ssdTempMx="0" 402 | local ssdComp="0" 403 | local hbaNum 404 | local hbaTempCur 405 | local hbaTempAv="0" 406 | 407 | if [ ! "${#ssdName[@]}" = "0" ]; then 408 | echo -e "Current SSD setpoint temp:\t$(targetTemp "${targetSSDriveTemp}")°C" 409 | fi 410 | echo -e "Current HD setpoint temp:\t$(targetTemp "${targetHDriveTemp}")°C\n" 411 | 412 | if [ ! "${#hbaName[@]}" = "0" ]; then 413 | for hbaNum in "${hbaName[@]}"; do 414 | # Get the temp for the current hba. 415 | if [ -c "/dev/${hbaNum}" ]; then 416 | if echo "${hbaNum}" | grep -q "mpr"; then 417 | # See https://gist.github.com/dak180/cd44e9957e1c4180e7eb6eb000716ee2 418 | hbaTempCur="$(${lsi_temp} "/dev/${hbaNum}" | grep 'IOC' | cut -d ' ' -f 3)" 419 | elif echo "${hbaNum}" | grep -q "ses"; then 420 | hbaTempCur="$(sesutil map -u "/dev/${hbaNum}" --libxo:J | jq --arg hbaNum "${hbaNum}" -Mre '.sesutil.enclosures[] | select(.enc == $hbaNum) | .elements[] | select((.type == "Temperature Sensor") and (.status == "OK")) | .extra_status.temperature | values')" 421 | fi 422 | else 423 | hbaTempCur="$(ipmiSens "${hbaNum}")" 424 | fi 425 | # Start adding temps for an average. 426 | hbaTempAv="$(( hbaTempAv + hbaTempCur ))" 427 | 428 | # Echo HBA's current temp 429 | if [ -c "/dev/${hbaNum}" ]; then 430 | echo -e "${hbaNum} Temp:\t${hbaTempCur}°C" 431 | else 432 | echo -e "HBA Temp:\t${hbaTempCur}°C" 433 | fi 434 | done 435 | echo -e " " 436 | # Divide by number of drives for average. 437 | hbaTempAv="$(bc <<< "scale=3;${hbaTempAv} / ${#hbaName[@]}")" 438 | fi 439 | 440 | for hdNum in "${hdName[@]}"; do 441 | # Get the temp for the current drive. 442 | hdTempCur="$(smartctl -aj "/dev/${hdNum}" | jq -Mre '.temperature.current | values')" 443 | # Start adding temps for an average. 444 | hdTempAv="$(( hdTempAv + hdTempCur ))" 445 | 446 | # Track non responsive drives 447 | if [ -z "${hdTempCur}" ]; then 448 | hdComp="$(( hdComp + 1 ))" 449 | echo -e "${hdNum} Temp:\tN/A" 450 | else 451 | # Echo HD's current temp 452 | echo -e "${hdNum} Temp:\t${hdTempCur}°C" 453 | fi 454 | 455 | 456 | done 457 | # Divide by number of drives for average. 458 | hdTempAv="$(bc <<< "scale=3;${hdTempAv} / (${#hdName[@]} - ${hdComp})")" 459 | 460 | 461 | if [ ! "${#ssdName[@]}" = "0" ]; then 462 | for ssdNum in "${ssdName[@]}"; do 463 | # Get the temp for the current drive. 464 | ssdTempCur="$(smartctl -aj "/dev/${ssdNum}" | jq -Mre '.temperature.current | values')" 465 | # Start adding temps for an average. 466 | ssdTempAv="$(( ssdTempAv + ssdTempCur ))" 467 | 468 | # Track non responsive drives 469 | if [ -z "${ssdTempCur}" ]; then 470 | ssdComp="$(( ssdComp + 1 ))" 471 | echo -e "${ssdNum} Temp:\tN/A" 472 | else 473 | # Echo SSD's current temp 474 | echo -e "${ssdNum} Temp:\t${ssdTempCur}°C" 475 | fi 476 | 477 | # Keep track of the lowest current temp 478 | if [ "${ssdTempMn:="${ssdTempCur}"}" -lt "${ssdTempCur}" ]; then 479 | true 480 | else 481 | ssdTempMn="${ssdTempCur}" 482 | fi 483 | 484 | # Keep track of the highest current temp 485 | if [ "${ssdTempMx}" -gt "${ssdTempCur}" ]; then 486 | true 487 | else 488 | ssdTempMx="${ssdTempCur}" 489 | fi 490 | done 491 | 492 | if [ "$((ssdTempMx - ssdTempMn))" -ge "10" ]; then 493 | ssdComp="$(( ssdComp + 1 ))" 494 | fi 495 | 496 | # Divide by number of drives for average. 497 | ssdTempAv="$(bc <<< "scale=3;${ssdTempAv} / (${#ssdName[@]} - ${ssdComp})")" 498 | fi 499 | 500 | 501 | echo -e "\nAverage HD Temp: ${hdTempAv}°C" 502 | if [ ! "${#ssdName[@]}" = "0" ]; then 503 | echo -e "Average SSD Temp: ${ssdTempAv}°C" 504 | fi 505 | if [ ! "${#hbaName[@]}" = "0" ]; then 506 | echo -e "HBA Temp Average: ${hbaTempAv}°C" 507 | fi 508 | } 509 | 510 | 511 | # Print fan info 512 | # shellcheck disable=SC2086 513 | function infoFans { 514 | ipmiRead 515 | local cpuFan 516 | local intakeFan 517 | local outputFan 518 | local countd 519 | 520 | for cpuFan in "${!CPU_FAN[@]}"; do 521 | countd=$((cpuFan+1)) 522 | if [ "${CPU_FAN[${cpuFan}]}" = "0" ]; then 523 | CPU_FAN[${cpuFan}]="auto" 524 | else 525 | CPU_FAN[${cpuFan}]="${CPU_FAN[${cpuFan}]}%" 526 | fi 527 | echo -e "CPU_FAN${countd}\t${CPU_FAN[${cpuFan}]}\t$(ipmiSens "CPU_FAN${countd}") rpm" 528 | done 529 | 530 | for intakeFan in "${!FRNT_FAN[@]}"; do 531 | countd=$((intakeFan+1)) 532 | if [ "${FRNT_FAN[${intakeFan}]}" = "0" ]; then 533 | FRNT_FAN[${intakeFan}]="auto" 534 | else 535 | FRNT_FAN[${intakeFan}]="${FRNT_FAN[${intakeFan}]}%" 536 | fi 537 | echo -e "FRNT_FAN${countd}\t${FRNT_FAN[${intakeFan}]}\t$(ipmiSens "FRNT_FAN${countd}") rpm" 538 | done 539 | 540 | for outputFan in "${!REAR_FAN[@]}"; do 541 | countd=$((outputFan+1)) 542 | if [ "${REAR_FAN[${outputFan}]}" = "0" ]; then 543 | REAR_FAN[${outputFan}]="auto" 544 | else 545 | REAR_FAN[${outputFan}]="${REAR_FAN[${outputFan}]}%" 546 | fi 547 | echo -e "REAR_FAN${countd}\t${REAR_FAN[${outputFan}]}\t$(ipmiSens "REAR_FAN${countd}") rpm" 548 | done 549 | } 550 | 551 | 552 | # Set fan duty levels 553 | function setFanDuty { 554 | local cpuFan 555 | local hbaFan 556 | local intakeFan 557 | local outputFan 558 | local cpuFanSet 559 | local hbaFanSet 560 | local intakeFanSet 561 | local outputFanSet 562 | cpuFanSet="$(roundR "${1}")" 563 | hbaFanSet="$(roundR "${2}")" 564 | intakeFanSet="$(roundR "${3}")" 565 | ssdFanSet="$(roundR "${4}")" 566 | outputFanSet="$(bc <<< "scale=0;${intakeFanSet} - ${difFanDuty}")" 567 | 568 | for cpuFan in "${!CPU_FAN[@]}"; do 569 | CPU_FAN[${cpuFan}]="${cpuFanSet}" 570 | done 571 | 572 | for intakeFan in "${!FRNT_FAN[@]}"; do 573 | FRNT_FAN[${intakeFan}]="${intakeFanSet}" 574 | done 575 | 576 | for outputFan in "${!REAR_FAN[@]}"; do 577 | REAR_FAN[${outputFan}]="${outputFanSet}" 578 | done 579 | 580 | for hbaFan in "${!HBA_FAN[@]}"; do 581 | HBA_FAN[${hbaFan}]="${hbaFanSet}" 582 | done 583 | 584 | for ssdFan in "${!SSD_FAN[@]}"; do 585 | SSD_FAN[${ssdFan}]="${ssdFanSet}" 586 | done 587 | } 588 | 589 | 590 | # The proportional calculation 591 | function proportionalK { 592 | local errorK="${1}" 593 | local controlOuput 594 | 595 | controlOuput="$(bc <<< "scale=3;${errorK} * ${Kp}")" 596 | echo "${controlOuput}" 597 | } 598 | 599 | # The integral calculation 600 | function integralK { 601 | local errorK="${1}" 602 | local prevIntegralVal="${2}" 603 | local controlStep 604 | local controlOuput 605 | 606 | controlStep="$(bc <<< "scale=3;(${errorK} * ${diskCheckTempInterval}) + ${prevIntegralVal}")" 607 | controlOuput="$(bc <<< "scale=3;(${Ki} * ${controlStep}) / 1")" 608 | echo "${controlOuput}" 609 | } 610 | 611 | # The derivative calculation 612 | function derivativeK { 613 | local errorK="${1}" 614 | local prevErrorK="${2}" 615 | local controlOuput 616 | 617 | controlOuput="$(bc <<< "scale=3;${Kd} * ((${errorK} - ${prevErrorK}) / ${diskCheckTempInterval})")" 618 | echo "${controlOuput}" 619 | } 620 | 621 | 622 | # 623 | # Main Script Starts Here 624 | # 625 | 626 | while getopts ":c:tfd" OPTION; do 627 | case "${OPTION}" in 628 | c) 629 | configFile="${OPTARG}" 630 | ;; 631 | t) 632 | scAction="temps" 633 | ;; 634 | f) 635 | scAction="fans" 636 | ;; 637 | d) 638 | scAction="daemon" 639 | ;; 640 | ?) 641 | # If an unknown flag is used (or -?): 642 | echo "${0} {-c configFile} {-t|-f|-d}" >&2 643 | exit 1 644 | ;; 645 | esac 646 | done 647 | 648 | if [ -z "${configFile}" ]; then 649 | echo "Please specify a config file location." >&2 650 | exit 1 651 | elif [ ! -f "${configFile}" ]; then 652 | fcConfig 653 | fi 654 | 655 | # Source external config file 656 | # shellcheck source=./FanConfig 657 | . "${configFile}" 658 | 659 | # Check if needed software is installed. 660 | PATH="${PATH}:/usr/local/sbin:/usr/local/bin" 661 | commands=( 662 | grep 663 | sed 664 | cut 665 | sleep 666 | bc 667 | smartctl 668 | jq 669 | ipmitool 670 | sysctl 671 | sesutil 672 | ) 673 | for command in "${commands[@]}"; do 674 | if ! type "${command}" &> /dev/null; then 675 | echo "${command} is missing, please install" >&2 676 | exit 100 677 | fi 678 | done 679 | 680 | 681 | # Do not run if the config file has not been edited. 682 | if [ ! "${defaultFile}" = "0" ]; then 683 | echo "Please edit the config file for your setup" >&2 684 | exit 1 685 | fi 686 | 687 | 688 | # Must be run as root 689 | if [ ! "$(whoami)" = "root" ]; then 690 | echo "Must be run as root." >&2 691 | exit 1 692 | fi 693 | 694 | 695 | # 696 | # Alternate modes 697 | # 698 | 699 | if [ -z "${scAction}" ]; then 700 | echo "Please specify an action." >&2 701 | exit 1 702 | elif [ "${scAction}" = "temps" ]; then 703 | infoTemps 704 | exit 0 705 | elif [ "${scAction}" = "fans" ]; then 706 | infoFans 707 | exit 0 708 | elif [ "${scAction}" = "daemon" ]; then 709 | : # nothing currently needed here 710 | fi 711 | 712 | 713 | # Set fans to auto on exit 714 | function scExit { 715 | setFanDuty "${autoFanDuty}" "${autoFanDuty}" "${autoFanDuty}" "${autoFanDuty}" 716 | ipmiWrite 717 | exit 0 718 | } 719 | 720 | trap "scExit" EXIT 721 | 722 | 723 | # 724 | # Setup for main loop. 725 | # 726 | 727 | # Get current duty levels. 728 | ipmiRead 729 | 730 | # Start fans at max 731 | setFanDuty "100" "100" "100" "100" 732 | 733 | if ! ipmiWrite; then 734 | exit 1 735 | fi 736 | 737 | 738 | # Get number of CPUs 739 | numberCPU="$(bc <<< "$(sysctl -n hw.ncpu) - 1")" 740 | 741 | # Initialize previous run vars. 742 | : "${prevHDErrorK:="0"}" 743 | : "${prevHDProportionalVal:="0"}" 744 | : "${prevHDIntegralVal:="0"}" 745 | : "${prevHDDerivativeVal:="0"}" 746 | : "${prevHDControlOutput:="100"}" 747 | 748 | : "${prevSSDErrorK:="0"}" 749 | : "${prevSSDProportionalVal:="0"}" 750 | : "${prevSSDIntegralVal:="0"}" 751 | : "${prevSSDDerivativeVal:="0"}" 752 | : "${prevSSDControlOutput:="100"}" 753 | 754 | : "${prevHBAErrorK:="0"}" 755 | : "${prevHBAProportionalVal:="0"}" 756 | : "${prevHBAIntegralVal:="0"}" 757 | : "${prevHBADerivativeVal:="0"}" 758 | : "${prevHBAControlOutput:="100"}" 759 | 760 | 761 | # 762 | # Main Loop. 763 | # 764 | 765 | 766 | while true; do 767 | # Calculate HD fan settings 768 | # 769 | HDsetPoint="$(targetTemp "${targetHDriveTemp}")" 770 | HDprocessVar="$(hdTemp)" 771 | 772 | # Get the error. 773 | HDerrorK="$(bc <<< "scale=3;${HDprocessVar} - ${HDsetPoint}")" 774 | 775 | # Compute an unqualified control output (P+I+D). 776 | HDproportionalVal="$(proportionalK "${HDerrorK}")" 777 | HDintegralVal="$(integralK "${HDerrorK}" "${prevHDIntegralVal}")" 778 | HDderivativeVal="$(derivativeK "${HDerrorK}" "${prevHDErrorK}")" 779 | 780 | unQualHDControlOutput="$(bc <<< "scale=3;${prevHDControlOutput} + ${HDproportionalVal} + ${HDintegralVal} + ${HDderivativeVal}")" 781 | 782 | # Qualify the output to ensure we are inside the constraints. 783 | qualHDMinFanDuty="$(bc <<< "${minFanDuty} + ${difFanDuty}")" 784 | qualHDMinFanDuty="$(roundR "${qualHDMinFanDuty}")" 785 | qualHDControlOutput="$(roundR "${unQualHDControlOutput}")" 786 | 787 | if [ "${qualHDControlOutput}" -lt "${qualHDMinFanDuty}" ]; then 788 | qualHDControlOutput="${qualHDMinFanDuty}" 789 | elif [ "${qualHDControlOutput}" -gt "${maxFanDuty}" ]; then 790 | qualHDControlOutput="${maxFanDuty}" 791 | fi 792 | 793 | 794 | if [ ! "${#ssdName[@]}" = "0" ]; then 795 | # Calculate SSD fan settings 796 | # 797 | SSDsetPoint="$(targetTemp "${targetSSDriveTemp}")" 798 | SSDprocessVar="$(ssdTemp)" 799 | 800 | # Get the error. 801 | SSDerrorK="$(bc <<< "scale=3;${SSDprocessVar} - ${SSDsetPoint}")" 802 | 803 | # Compute an unqualified control output (P+I+D). 804 | SSDproportionalVal="$(proportionalK "${SSDerrorK}")" 805 | SSDintegralVal="$(integralK "${SSDerrorK}" "${prevSSDIntegralVal}")" 806 | SSDderivativeVal="$(derivativeK "${SSDerrorK}" "${prevSSDErrorK}")" 807 | 808 | unQualSSDControlOutput="$(bc <<< "scale=3;${prevSSDControlOutput} + ${SSDproportionalVal} + ${SSDintegralVal} + ${SSDderivativeVal}")" 809 | 810 | # Qualify the output to ensure we are inside the constraints. 811 | qualSSDMinFanDuty="$(bc <<< "${minFanDuty} + ${difFanDuty}")" 812 | qualSSDMinFanDuty="$(roundR "${qualSSDMinFanDuty}")" 813 | qualSSDControlOutput="$(roundR "${unQualSSDControlOutput}")" 814 | 815 | if [ "${qualSSDControlOutput}" -lt "${qualSSDMinFanDuty}" ]; then 816 | qualSSDControlOutput="${qualSSDMinFanDuty}" 817 | elif [ "${qualSSDControlOutput}" -gt "${maxFanDuty}" ]; then 818 | qualSSDControlOutput="${maxFanDuty}" 819 | fi 820 | fi 821 | 822 | 823 | # Calculate HBA fan settings 824 | # 825 | HBAsetPoint="${targetHBATemp}" 826 | HBAprocessVar="$(hbaTemp)" 827 | 828 | # Get the error. 829 | HBAerrorK="$(bc <<< "scale=3;${HBAprocessVar} - ${HBAsetPoint}")" 830 | 831 | # Compute an unqualified control output (P+I+D). 832 | HBAproportionalVal="$(proportionalK "${HBAerrorK}")" 833 | HBAintegralVal="$(integralK "${HBAerrorK}" "${prevHBAIntegralVal}")" 834 | HBAderivativeVal="$(derivativeK "${HBAerrorK}" "${prevHBAErrorK}")" 835 | 836 | unQualHBAControlOutput="$(bc <<< "scale=3;${prevHBAControlOutput} + ${HBAproportionalVal} + ${HBAintegralVal} + ${HBAderivativeVal}")" 837 | 838 | # Qualify the output to ensure we are inside the constraints. 839 | qualHBAMinFanDuty="$(roundR "${minFanDuty}")" 840 | qualHBAControlOutput="$(roundR "${unQualHBAControlOutput}")" 841 | 842 | if [ "${qualHBAControlOutput}" -lt "${qualHBAMinFanDuty}" ]; then 843 | qualHBAControlOutput="${qualHBAMinFanDuty}" 844 | elif [ "${qualHBAControlOutput}" -gt "${maxFanDuty}" ]; then 845 | qualHBAControlOutput="${maxFanDuty}" 846 | fi 847 | 848 | 849 | # We only need to set the fans if something changes. 850 | if [ ! "${prevHDControlOutput}" = "${qualHDControlOutput}" ] || [ ! "${prevHBAControlOutput}" = "${qualHBAControlOutput}" ] || [ ! "${prevSSDControlOutput}" = "${qualSSDControlOutput}" ]; then 851 | 852 | # For the case where the same fans are used for SSDs and HDDs 853 | if [ "${mixedFans}" = "1" ]; then 854 | if [ "${qualSSDControlOutput}" -gt "${qualHDControlOutput}" ]; then 855 | qualDiskControlOutput="${qualSSDControlOutput}" 856 | else 857 | qualDiskControlOutput="${qualHDControlOutput}" 858 | fi 859 | fi 860 | 861 | # Set the duty levels for each fan type. 862 | if [ "${mixedFans}" = "1" ]; then 863 | setFanDuty "${autoFanDuty}" "${qualHBAControlOutput}" "${qualDiskControlOutput}" "${qualDiskControlOutput}" 864 | else 865 | setFanDuty "${autoFanDuty}" "${qualHBAControlOutput}" "${qualHDControlOutput}" "${qualSSDControlOutput}" 866 | fi 867 | 868 | 869 | # Write out the new duty levels to ipmi. 870 | # shellcheck disable=SC2086 871 | if ! ipmiWrite; then 872 | exit 1 873 | fi 874 | fi 875 | 876 | # Set vars for next run 877 | prevHDErrorK="${HDerrorK}" 878 | prevHDProportionalVal="${HDproportionalVal}" 879 | prevHDIntegralVal="${HDintegralVal}" 880 | prevHDDerivativeVal="${HDderivativeVal}" 881 | prevHDControlOutput="${qualHDControlOutput}" 882 | 883 | prevSSDErrorK="${SSDerrorK}" 884 | prevSSDProportionalVal="${SSDproportionalVal}" 885 | prevSSDIntegralVal="${SSDintegralVal}" 886 | prevSSDDerivativeVal="${SSDderivativeVal}" 887 | prevSSDControlOutput="${qualSSDControlOutput}" 888 | 889 | prevHBAErrorK="${HBAerrorK}" 890 | prevHBAProportionalVal="${HBAproportionalVal}" 891 | prevHBAIntegralVal="${HBAintegralVal}" 892 | prevHBADerivativeVal="${HBAderivativeVal}" 893 | prevHBAControlOutput="${qualHBAControlOutput}" 894 | 895 | 896 | sleep "$(bc <<< "scale=0;60 * ${diskCheckTempInterval}" | sed -e 's:.0$::')" 897 | done 898 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 - 2023 by dak180, Keith Nash and contributors. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # A Collection of Scripts For TrueNAS # 2 | 3 | ## FanControl.tool ## 4 | 5 | `FanControl.tool {-c configFile} {-t|-f|-d}` 6 | 7 | This tool is meant to control case fans via ipmi to maintain target disk and HBA temperatures using a PID control loop on TrueNAS Core. It also allows you to get a readout of current temps and fan speeds. 8 | 9 | The first run of the script will write out a default config file to the specified location; this file will need to be edited to conform to your system. 10 | 11 | Most of the first sections of the config file should be fairly self explanatory, so starting with `lsi_temp` in the **List of HBAs** section: this is the path to a compiled binary of the [lsi_temp](https://gist.github.com/dak180/cd44e9957e1c4180e7eb6eb000716ee2) helper program which gets the temp of LSI HBA cards that support it (SAS 3+ generally) 12 | 13 | `hbaName` is a list of both HBAs and expanders that support temp sensors. 14 | 15 | **Temp sensors**: This section allows you to specify what the motherboard temp sensors will be used for. 16 | 17 | The `hbaTempSens` array is useful if your motherboard supports thermal probes and you have a HBA card that does not support temperature reporting. 18 | 19 | The `ambTempSens` array is to specify which sensors mesure the internal ambient temperature of the case. 20 | 21 | **IPMI Fan Commands**: This section allows you to customise the commands to both read and write fan speeds. All of these commands _will_ need to be customised for both the motherboard you are using and how fans are attached to it. 22 | 23 | The `ipmiWrite` function is used to write the PWM levels for each fan as a value from 0 - 100; this command is specific to a particular model of board, consult your board manufacture to get the correct command. 24 | 25 | The `ipmiRead` function is used to read the PWM levels for each fan as a value from 0 - 100 in hexadecimal; this command is specific to a particular model of board, consult your board manufacture to get the correct command. 26 | 27 | It is suggested that following invocation be used to run this script from a Post Init task: 28 | ```bash 29 | /usr/sbin/daemon -t "FanControl" -P "/var/run/daemon-FanControl.pid" -p "/var/run/FanControl.pid" -Ss "info" -T "FanControl" -R "60" /FanControl.tool -dc "/FanConfig" 30 | ``` 31 | 32 | ## ipfw.rules ## 33 | 34 | A script to setup `ipfw` to force all non lan trafic from the `transmission` user through the first listed `tun` device. For use with the `pia-port-forward.sh` script. 35 | 36 | ## jls.tool ## 37 | 38 | This a script to automate the creation of jails; modification of the generated config file be required for your setup. 39 | 40 | ## pia-port-forward.sh ## 41 | 42 | This is a script to maintain a pia vpn connection with an open port for transmission. It is intended to be run via cron at intervals of less than 15 mins from within the jail running transmission and openvpn. 43 | 44 | This script generally expects to be run form a jail created via `jls.tool -c jls.cfg -t transmission`. 45 | 46 | The `vpnDir` will need to be set to the location of the openvpn config directory. 47 | 48 | The `firewallScript` needs to be set to the location of the `ipfw.rules` file. 49 | 50 | ## search.cmd ## 51 | 52 | A command to initiate crawls from within the **search** jail as setup by `jls.tool -c jls.cfg -t search`. 53 | 54 | ## suite-definition.xml ## 55 | 56 | A configuration file for the phoronix-test package. 57 | 58 | ## tlerActiveation.tool ## 59 | 60 | A script to activate Time Limited Error Recovery on drives that do not remember this setting between reboots. This is intended to be run as a Post Init task. 61 | -------------------------------------------------------------------------------- /ipfw.rules: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/bash 2 | # Config 3 | 4 | # Set rules command prefix 5 | cmd="ipfw -q add" 6 | user="transmission" 7 | localLan="192.168.0.0/16" 8 | 9 | # Block connections by User until the vpn is ready 10 | ipfw -q -f flush add 11 | ${cmd} 00193 deny all from any to any uid ${user} 12 | 13 | # Get the tunnel id dynamically 14 | vpn="$(ifconfig | grep -v "groups" | grep "tun" | cut -d ":" -f1 | tail -n 1)" 15 | while [ -z "${vpn}" ] && [ "${try:="0"}" -le "20" ]; do 16 | vpn="$(ifconfig | grep -v "groups" | grep "tun" | cut -d ":" -f1 | tail -n 1)" 17 | try="$(( try + 1 ))" 18 | sleep 3 19 | done 20 | 21 | # Flush out the list before we begin 22 | ipfw -q -f flush add 23 | 24 | # Allow all local traffic on the loopback interface 25 | ${cmd} 00001 allow all from any to any via lo0 26 | 27 | # Allow any connection to/from VPN interface 28 | ${cmd} 00010 allow all from any to any via "${vpn[@]:="tun1"}" 29 | 30 | # Allow connection to/from LAN by User 31 | ${cmd} 00101 allow all from me to ${localLan} uid ${user} 32 | ${cmd} 00102 allow all from ${localLan} to me uid ${user} 33 | 34 | # Deny any User connection outside LAN that does not use VPN 35 | ${cmd} 00193 deny all from any to any uid ${user} 36 | -------------------------------------------------------------------------------- /jls.tool: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # shellcheck disable=SC2236,SC2086,SC2068,SC2317,SC2155 3 | 4 | # Config 5 | 6 | # Write out a default config file 7 | function jConfig { 8 | tee > "${configFile}" <<"EOF" 9 | # Set this to 0 to enable 10 | defaultFile="1" 11 | 12 | # Jail creation config file 13 | configVers="0" # Do not edit this. 14 | 15 | # Resolver lines for different vlans for jails to connect to (vlans must already be defined in the network -> interface section of the gui). Necessary only if the vlan is different than the one used for the web interface. 16 | # If you do not have vlans this can be ignored. 17 | resolver60="search local.dak180.com local;nameserver 192.168.60.1" 18 | resolver04="search local.dak180.com local;nameserver 192.168.4.1" 19 | 20 | # The release to base jails on. 21 | ioRelease="13.2-RELEASE" # LATEST 22 | 23 | # Common paths to be mounted into jails (relative to the host). 24 | mediaPth="/mnt/data/Media" # path to media; will be mounted to `/media` in jails 25 | jDataPath="/mnt/jails/Data" # prefix path to where persistant jail application data will be ie: `/mnt/jails/Data/znc` these datasets will need to be created prior to making the jail 26 | gitPath="/mnt/data/git" # a location for git repos so a different Record Size can be set 27 | backupPth="/mnt/data/Backups" # prefix path to backup locations ie: `/mnt/data/Backups/plex` 28 | scriptPth="/mnt/jails/scripts" # path to a common set of scripts 29 | thingPath="/mnt/data/Things" # path to a general SMB share 30 | userPth="/mnt/jails/users/dak180" # path to a full user directory on the base system 31 | proxPth="/mnt/data/pkg-cache" # path to a dataset to cache pkg downloads 32 | 33 | # Common paths in jails (relative to the jail). 34 | usrpth="/mnt/scripts/user" # where user files are loaded into jails ie: .bashrc .profile .nanorc .config/* 35 | 36 | # Common Package list that will be installed into every jail 37 | tee "/tmp/pkg.json" << EOL 38 | { 39 | "pkgs": [ 40 | "bash", 41 | "bash-completion", 42 | "tmux", 43 | "wget", 44 | "curl", 45 | "nano", 46 | "sudo", 47 | "logrotate", 48 | "fortune-mod-freebsd-classic", 49 | "fortune-mod-bofh", 50 | "pkg-provides" 51 | ] 52 | } 53 | 54 | EOL 55 | 56 | ##### Jail specific settings 57 | # See https://www.freebsd.org/cgi/man.cgi?iocage#PROPERTIES for some more details on what can be set in . 58 | 59 | # Plex 60 | { 61 | # Checklist before creating this jail: 62 | # Ensure a group named `jailmedia` is created on the main system with GID `1001` 63 | # Ensure a user named `plex` is created on the main system with UID `972` 64 | # Ensure a user named `tautulli` is created on the main system with UID `892` 65 | # ${mediaPth} is set and is r/w by `jailmedia` 66 | # ${scriptPth} is set and is r/w by `jailmedia` 67 | # ${jDataPath}/plex is set and is owned by `plex` 68 | # ${backupPth}/plex is set and is owned by `plex` 69 | # ${jDataPath}/Tautulli is set and is owned by `tautulli` 70 | # See https://www.truenas.com/community/threads/activating-plex-hardware-acceleration.75391/#post-525442 for hardware transcoding setup 71 | 72 | 73 | # In this example we are disabling ipv6, setting the name of the bridge we are connecting to (or creating), what interface our trafic will go through (in this case the same as the web interface), and set the use of DHCP and a fixed MAC address pair to go with it. 74 | _plex=( 75 | vnet="1" 76 | allow_raw_sockets="1" 77 | ip6="disable" 78 | interfaces="vnet0:bridge0" 79 | vnet_default_interface="vlan10" 80 | bpf="1" 81 | dhcp="1" 82 | vnet0_mac="B213DD984A80 B213DD984A7F" 83 | ) 84 | 85 | # Setting a custom devfs rule set; used for setting up hardware transcodes, comment to disable 86 | _plexDevfs="109" 87 | 88 | if [ ! -z "${_plex_devfs}" ]; then 89 | _plex+=( 90 | devfs_ruleset="${_plex_devfs}" 91 | ) 92 | fi 93 | 94 | } 95 | 96 | # Transmission 97 | { 98 | torntPath="/mnt/data/torrents" # a temp location for torrents to land so a different Record Size can be set 99 | # Checklist before creating this jail: 100 | # Ensure a group named `jailmedia` is created on the main system with GID `1001` 101 | # Ensure a user named `transmission` is created on the main system with UID `921` 102 | # ${mediaPth} is set and is r/w by `jailmedia` 103 | # ${scriptPth} is set and is r/w by `jailmedia` 104 | # ${jDataPath}/transmission is set and is owned by `transmission` 105 | # ${jDataPath}/openvpn is set and is owned by `root` 106 | # ${torntPath} is set and is owned by `transmission` and is r/w by `jailmedia` 107 | # ${thingPath}/Torrents is set and is r/w by `jailmedia` 108 | # pia-port-forward.sh, ipfw.rules, transmission.crontab and transmission.logrotate are in ${scriptPth}/trans 109 | 110 | 111 | # In this example we are disabling ipv6, allowing tun interfaces, setting the name of the bridge we are connecting to (or creating), what interface our trafic will go through (in this case the different from the web interface so we set the appropriate resolver), and set the use of DHCP, and a fixed MAC address pair to go with it. 112 | _transmission=( 113 | vnet="1" 114 | allow_raw_sockets="1" 115 | ip6="disable" 116 | allow_tun="1" 117 | interfaces="vnet0:bridge60" 118 | vnet_default_interface="vlan60" 119 | resolver="${resolver60}" 120 | bpf="1" 121 | dhcp="1" 122 | vnet0_mac="4a3a78771683 4a3a78771682" 123 | ) 124 | 125 | # Name of openvpn config file 126 | _transmission_openvpn_configfile="openvpn.conf" 127 | 128 | # Static route to allow cross vlan communication; comment to disable 129 | _transmission_static="192.168.0.0/16 192.168.60.1" 130 | 131 | } 132 | 133 | # Unifi 134 | { 135 | # Checklist before creating this jail: 136 | # Ensure a user named `unifi` is created on the main system with UID `975` 137 | # ${jDataPath}/unifi is set and is owned by `unifi` 138 | # ${scriptPth} is set and is r/w by `jailmedia` 139 | 140 | 141 | # In this example we are setting the name of the bridges we are connecting to (or creating), what interface our trafic will go through (in this case the different from the web interface so we set the appropriate resolver), and set the use of DHCP and a fixed MAC address pair to go with it. 142 | _unifi=( 143 | vnet="1" 144 | allow_raw_sockets="1" 145 | interfaces="vnet0:bridge0,vnet1:bridge4" 146 | vnet_default_interface="none" 147 | resolver="${resolver04}" 148 | bpf="1" 149 | dhcp="1" 150 | vnet0_mac="02ff608700b4 02ff608700b5" 151 | vnet1_mac="02ff60680091 02ff60680092" 152 | ) 153 | 154 | } 155 | 156 | # Netdata 157 | { 158 | # Checklist before creating this jail: 159 | # Ensure a user named `netdata` is created on the main system with UID `302` 160 | # ${jDataPath}/netdata is set and is owned by `netdata` 161 | # ${jDataPath}/netdata/config is set 162 | # ${jDataPath}/netdata/cache is set 163 | # ${jDataPath}/netdata/db is set 164 | # ${jDataPath}/netdata/smartd is set 165 | # ${scriptPth} is set and is r/w by `jailmedia` 166 | # netdata.crontab and netdata.logrotate are in ${scriptPth}/netdata 167 | # Add a tunable on the main system: type: rc.conf | Variable: `smartd_daemon_flags` | Value: `${smartd_daemon_flags} --attributelog=/netdata/smartd/` 168 | 169 | 170 | # In this example we are setting NAT and the port forwards, setting the name of the bridge we are connecting to (or creating), and a fixed MAC address pair to go with it. 171 | _netdata=( 172 | allow_raw_sockets="1" 173 | nat="1" 174 | nat_forwards="tcp(19999:19999)" 175 | interfaces="vnet0:bridge0" 176 | vnet0_mac="02ff602be694 02ff602be695" 177 | ) 178 | 179 | } 180 | 181 | # PVR 182 | { 183 | # Checklist before creating this jail: 184 | # Ensure a group named `jailmedia` is created on the main system with GID `1001` 185 | # Ensure a user named `sonarr` is created on the main system with UID `351` 186 | # Ensure a user named `radarr` is created on the main system with UID `352` 187 | # Ensure a user named `jackett` is created on the main system with UID `354` 188 | # Ensure a user named `bazarr` is created on the main system with UID `357` 189 | # ${mediaPth} is set and is r/w by `jailmedia` 190 | # ${scriptPth} is set and is r/w by `jailmedia` 191 | # ${torntPath} is set and is owned by `transmission` and is r/w by `jailmedia` 192 | # ${thingPath}/Torrents is set and is r/w by `jailmedia` 193 | # ${jDataPath}/sonarr is set and is owned by `sonarr` 194 | # ${jDataPath}/radarr is set and is owned by `radarr` 195 | # ${jDataPath}/jackett is set and is owned by `jackett` 196 | # ${jDataPath}/bazarr is set and is owned by `bazarr` 197 | ### mono fixes (see: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=258709) 198 | # mono6.8-6.8.0.123.txz, py37-pillow-7.0.0.txz, py37-olefile-0.46.txz and ca-root-nss.crt are in ${scriptPth}/pvr 199 | 200 | 201 | # In this example we are disabling ipv6, setting the name of the bridge we are connecting to (or creating), what interface our trafic will go through (in this case the different from the web interface so we set the appropriate resolver), and set the use of DHCP, a fixed MAC address pair to go with it, and that the plex and transmission jails should start first. 202 | _pvr=( 203 | vnet="1" 204 | allow_raw_sockets="1" 205 | ip6="disable" 206 | interfaces="vnet0:bridge60" 207 | vnet_default_interface="vlan60" 208 | resolver="${resolver60}" 209 | bpf="1" 210 | dhcp="1" 211 | vnet0_mac="02ff60df8049 02ff60df804a" 212 | depends="plex transmission flaresolverr" 213 | ) 214 | 215 | } 216 | 217 | # FlareSolverr 218 | { 219 | # Checklist before creating this jail: 220 | # Ensure a group named `jailmedia` is created on the main system with GID `1001` 221 | # Ensure a group named `flaresolverr` is created on the main system with GID `2000` 222 | # Ensure a user named `flaresolverr` is created on the main system with UID `2000` 223 | # ${jDataPath}/flaresolverr is set and is owned by `flaresolverr` 224 | 225 | 226 | # In this example we are disabling ipv6, setting the name of the bridge we are connecting to (or creating), what interface our trafic will go through (in this case the different from the web interface so we set the appropriate resolver), and set the use of DHCP, a fixed MAC address pair to go with it. 227 | _flaresolverr=( 228 | vnet="1" 229 | allow_raw_sockets="1" 230 | ip6="disable" 231 | interfaces="vnet0:bridge60" 232 | vnet_default_interface="vlan60" 233 | resolver="${resolver60}" 234 | bpf="1" 235 | dhcp="1" 236 | vnet0_mac="02ff60717d7b 02ff60717d7c" 237 | ) 238 | 239 | } 240 | 241 | # ZNC 242 | { 243 | # Checklist before creating this jail: 244 | # Ensure a user named `znc` is created on the main system with UID `897` 245 | # ${jDataPath}/unifi is set and is owned by `znc` 246 | # ${scriptPth} is set and is r/w by `jailmedia` 247 | 248 | 249 | # In this example we are setting the name of the bridge we are connecting to (or creating), what interface our trafic will go through (in this case the same as the web interface), and set the use of DHCP and a fixed MAC address pair to go with it. 250 | _znc=( 251 | vnet="1" 252 | allow_raw_sockets="1" 253 | interfaces="vnet0:bridge0" 254 | vnet_default_interface="vlan10" 255 | bpf="1" 256 | dhcp="1" 257 | vnet0_mac="02ff609935af 02ff609935b0" 258 | ) 259 | 260 | } 261 | 262 | # Gitea 263 | { 264 | # Checklist before creating this jail: 265 | # Ensure a group named `jailmedia` is created on the main system with GID `1001` 266 | # Ensure a user named `git` is created on the main system with UID `211` and home directory set to `${userPth}/../git 267 | # Ensure a user named `git_daemon` is created on the main system with UID `964` 268 | # ${scriptPth} is set and is r/w by `jailmedia` 269 | # ${gitPath} is set and is owned by `git` and is r/w by `jailmedia` 270 | # ${jDataPath}/gitea is set and is owned by `git` 271 | # ${jDataPath}/gitea/etc is set 272 | # ${jDataPath}/gitea/share is set 273 | 274 | 275 | # In this example we are disabling ipv6, setting the name of the bridge we are connecting to (or creating), what interface our trafic will go through (in this case the same as the web interface), and set the use of DHCP, and a fixed MAC address pair to go with it. 276 | _gitea=( 277 | vnet="1" 278 | allow_raw_sockets="1" 279 | ip6="disable" 280 | interfaces="vnet0:bridge0" 281 | vnet_default_interface="vlan10" 282 | bpf="1" 283 | dhcp="1" 284 | vnet0_mac="02ff60757089 02ff6075708a" 285 | ) 286 | 287 | } 288 | 289 | # Search 290 | { 291 | # Checklist before creating this jail: 292 | # Ensure a group named `jailmedia` is created on the main system with GID `1001` 293 | # Ensure a user named `elasticsearch` is created on the main system with UID `965` 294 | # ${mediaPth} is set and is r/w by `jailmedia` 295 | # ${scriptPth} is set and is r/w by `jailmedia` 296 | # ${thingPath} is set and is r/w by `jailmedia` 297 | # ${jDataPath}/fscrawler is set and is owned by `elasticsearch` 298 | # Download https://github.com/dadoonet/fscrawler/releases/tag/fscrawler-2.7 and unzip in ${jDataPath}/fscrawler 299 | # ${jDataPath}/elasticsearch is set and is owned by `elasticsearch` 300 | # ${jDataPath}/elasticsearch/etc is set 301 | # ${jDataPath}/elasticsearch/db is set 302 | # ${jDataPath}/kibana is set and is owned by `elasticsearch` 303 | # elasticsearch.crontab is in ${scriptPth}/search 304 | 305 | 306 | # In this example we are disabling ipv6, setting the name of the bridge we are connecting to (or creating), what interface our trafic will go through (in this case the different from the web interface so we set the appropriate resolver), and set the use of DHCP, and a fixed MAC address pair to go with it. 307 | _search=( 308 | vnet="1" 309 | allow_raw_sockets="1" 310 | ip6="disable" 311 | interfaces="vnet0:bridge60" 312 | vnet_default_interface="vlan60" 313 | resolver="${resolver60}" 314 | bpf="1" 315 | dhcp="1" 316 | vnet0_mac="02ff60ae0444 02ff60ae0445" 317 | ) 318 | 319 | } 320 | 321 | # Test 322 | { 323 | # Checklist before creating this jail: 324 | # ${scriptPth} is set and is r/w by `jailmedia` 325 | 326 | 327 | # In this example we are setting NAT, and setting the name of the bridge we are connecting to (or creating). 328 | _test=( 329 | vnet="1" 330 | nat="1" 331 | interfaces="vnet0:bridge0" 332 | ) 333 | 334 | } 335 | 336 | # Port 337 | { 338 | # Checklist before creating this jail: 339 | # ${scriptPth} is set and is r/w by `jailmedia` 340 | 341 | 342 | # In this example we are setting the name of the bridge we are connecting to (or creating), what interface our trafic will go through (in this case the same as the web interface), and set the use of DHCP. 343 | _port=( 344 | vnet="1" 345 | interfaces="vnet0:bridge0" 346 | vnet_default_interface="vlan10" 347 | bpf="1" 348 | dhcp="1" 349 | ) 350 | 351 | } 352 | 353 | EOF 354 | } 355 | 356 | 357 | 358 | while getopts ":c:t:n:" OPTION; do 359 | case "${OPTION}" in 360 | c) 361 | configFile="${OPTARG}" 362 | ;; 363 | t) 364 | jlType="${OPTARG}" 365 | ;; 366 | n) 367 | jlNType="${OPTARG}" 368 | ;; 369 | ?) 370 | # If an unknown flag is used (or -?): 371 | echo "${0} -c -t {-n }" >&2 372 | exit 1 373 | ;; 374 | esac 375 | done 376 | 377 | if [ -z "${configFile}" ]; then 378 | echo "Please specify a config file location; if none exist one will be created." >&2 379 | exit 1 380 | elif [ ! -f "${configFile}" ]; then 381 | jConfig 382 | exit 0 383 | elif [ -z "${jlType}" ]; then 384 | echo "Please specify a jail type." >&2 385 | exit 1 386 | fi 387 | 388 | # shellcheck source=./jls.cfg 389 | . "${configFile}" 390 | 391 | # Do not run if the config file has not been edited. 392 | if [ ! "${defaultFile}" = "0" ]; then 393 | echo "Please edit the config file for your setup" >&2 394 | exit 1 395 | elif [ ! "${configVers}" = "0" ]; then 396 | mv "${configFile}" "${configFile}.bak" 397 | jConfig 398 | echo "The config has been changed please update it for your setup" >&2 399 | exit 1 400 | fi 401 | 402 | 403 | 404 | function portS { 405 | sudo iocage pkg "${jlName}" install -y svnup || { echo "Failed to install packages." >&2; exit 1;} 406 | sudo iocage exec -f "${jlName}" -- 'cat /usr/local/etc/svnup.conf.sample | sed -e "s:#host=svn\.:host=svn\.:" > /usr/local/etc/svnup.conf' 407 | sudo iocage exec -f "${jlName}" -- "svnup ports -v 0" || { echo "Failed to install packages." >&2; exit 1;} 408 | sudo iocage exec -f "${jlName}" -- "cd /usr/ports/ports-mgmt/portmaster && make install clean" 409 | } 410 | 411 | function usrpths { 412 | # Sets up command prompt and nano defaults in the jails. 413 | local usrpth="/mnt/scripts/user" 414 | 415 | # Link files 416 | sudo iocage exec -f "${jlName}" -- "cd /root/ && ln -s \"${usrpth}/.profile\" .bashrc" 417 | sudo iocage exec -f "${jlName}" -- "cd /root/ && ln -fs .bashrc .profile" 418 | sudo iocage exec -f "${jlName}" -- "cd /root/ && ln -s \"${usrpth}/.nanorc\" .nanorc" 419 | sudo iocage exec -f "${jlName}" -- "cd /root/ && ln -s \"${usrpth}/.config\" .config" 420 | } 421 | 422 | function comn_mnt_pnts { 423 | # Sets script and user mount points 424 | local userName="$(basename "${userPth}")" 425 | sudo iocage exec -f "${jlName}" -- "mkdir -pv '/mnt/scripts/' '/mnt/pkg-cache' '/mnt/users/${userName}/'" 426 | sudo iocage fstab -a "${jlName}" "${scriptPth} /mnt/scripts/ nullfs rw 0 0" 427 | sudo iocage fstab -a "${jlName}" "${proxPth} /mnt/pkg-cache/ nullfs rw 0 0" 428 | sudo iocage fstab -a "${jlName}" "${userPth} /mnt/users/${userName} nullfs rw 0 0" 429 | } 430 | 431 | function jl_init { 432 | sudo iocage pkg "${jlName}" update && sudo iocage pkg "${jlName}" upgrade -y 433 | 434 | # Common group to coordinate permissions across multiple jails. 435 | sudo iocage exec -f "${jlName}" -- "pw groupadd -n jailmedia -g 1001" 436 | } 437 | 438 | function pkg_repo { 439 | # Set latest pkg repo 440 | sudo iocage exec -f "${jlName}" -- "mkdir -p /usr/local/etc/pkg/repos" 441 | sudo iocage exec -f "${jlName}" -- 'tee "/usr/local/etc/pkg/repos/FreeBSD.conf" << EOF 442 | 443 | FreeBSD: { 444 | url: "pkg+http://pkg.FreeBSD.org/\${ABI}/latest" 445 | } 446 | 447 | EOF' 448 | 449 | sudo iocage exec -f "${jlName}" -- 'echo "PKG_CACHEDIR: /mnt/pkg-cache" >> "/usr/local/etc/pkg.conf"' 450 | } 451 | 452 | 453 | # Prevent sudo timeout 454 | sudo -v # ask for sudo password up-front 455 | while true; do 456 | # Update user's timestamp without running a command 457 | sudo -nv; sleep "60" 458 | # Exit when the parent process is not running any more. In fact this loop 459 | # would be killed anyway after being an orphan(when the parent process 460 | # exits). But this ensures that and probably exits sooner. 461 | kill -0 $$ 2>/dev/null || exit 462 | done & 463 | 464 | 465 | # Jail Creation 466 | if [ "${jlType}" = "plex" ]; then 467 | jlName="plex" 468 | { 469 | 470 | # Create jail 471 | if ! sudo iocage create -b -n "${jlName}" -p "/tmp/pkg.json" -r "${ioRelease}" allow_mount="1" allow_mount_devfs="1" allow_set_hostname="1" enforce_statfs="1" "${_plex[@]}"; then 472 | exit 1 473 | fi 474 | 475 | # Set Mounts 476 | comn_mnt_pnts 477 | sudo iocage exec -f "${jlName}" -- 'mkdir -pv "/usr/local/plexdata/" "/mnt/dbBackup/" "/var/db/tautulli/"' 478 | 479 | sudo iocage fstab -a "${jlName}" "${mediaPth} /media/ nullfs rw 0 0" 480 | sudo iocage fstab -a "${jlName}" "${jDataPath}/plex /usr/local/plexdata/ nullfs rw 0 0" 481 | sudo iocage fstab -a "${jlName}" "${backupPth}/plex /mnt/dbBackup/ nullfs rw 0 0" 482 | sudo iocage fstab -a "${jlName}" "${jDataPath}/Tautulli /var/db/tautulli/ nullfs rw 0 0" 483 | 484 | # Generic Configuration 485 | pkg_repo 486 | usrpths 487 | jl_init 488 | if [ ! -f "${jDataPath}/plex/.bash_history" ]; then 489 | sudo touch "${jDataPath}/plex/.bash_history" 490 | fi 491 | sudo iocage exec -f "${jlName}" -- 'ln -sf "/usr/local/plexdata/.bash_history" "/root/.bash_history"' 492 | 493 | # Install packages 494 | sudo iocage pkg "${jlName}" install -y multimedia/plexmediaserver-plexpass tautulli ffmpeg yt-dlp py39-pycryptodomex AtomicParsley multimedia/libva-intel-driver multimedia/libva-intel-media-driver || { echo "Failed to install packages." >&2; exit 1;} 495 | 496 | # Set permissions 497 | sudo iocage exec -f "${jlName}" -- "pw groupmod jailmedia -m plex" 498 | sudo iocage exec -f "${jlName}" -- "pw groupmod -n video -m plex" 499 | 500 | # Enable Services 501 | sudo iocage exec -f "${jlName}" -- 'sysrc plexmediaserver_plexpass_enable="YES"' 502 | sudo iocage exec -f "${jlName}" -- 'sysrc plexmediaserver_plexpass_support_path="/usr/local/plexdata"' 503 | 504 | sudo iocage exec -f "${jlName}" -- 'sysrc tautulli_enable="YES"' 505 | 506 | sudo iocage exec -f "${jlName}" -- "service tautulli start" 507 | sudo iocage exec -f "${jlName}" -- "service plexmediaserver_plexpass start" 508 | 509 | # Set jail to start at boot. 510 | sudo iocage stop "${jlName}" 511 | sudo iocage set boot="1" "${jlName}" 512 | if [ ! -z "${_plex_devfs}" ]; then 513 | sudo iocage set devfs_ruleset="109" "${jlName}" 514 | fi 515 | 516 | # Check MAC Address 517 | sudo iocage get vnet0_mac "${jlName}" 518 | 519 | # Create initial snapshot 520 | sudo iocage snapshot "${jlName}" -n InitialConfiguration 521 | sudo iocage start "${jlName}" 522 | } 523 | elif [ "${jlType}" = "trans" ] || [ "${jlType}" = "transmission" ]; then 524 | jlName="transmission" 525 | { 526 | 527 | # Create jail 528 | if ! sudo iocage create -b -n "${jlName}" -p "/tmp/pkg.json" -r "${ioRelease}" allow_set_hostname="1" priority="3" "${_transmission[@]}"; then 529 | exit 1 530 | fi 531 | 532 | # Set Mounts 533 | comn_mnt_pnts 534 | sudo iocage exec -f "${jlName}" -- 'mkdir -pv "/mnt/incoming/" "/mnt/torrents/" "/mnt/transmission/" "/var/db/transmission/" "/usr/local/etc/openvpn/"' 535 | 536 | sudo iocage fstab -a "${jlName}" "${mediaPth} /mnt/incoming/ nullfs rw 0 0" 537 | sudo iocage fstab -a "${jlName}" "${torntPath} /mnt/torrents/ nullfs rw 0 0" 538 | sudo iocage fstab -a "${jlName}" "${thingPath}/Torrents /mnt/transmission/ nullfs rw 0 0" 539 | sudo iocage fstab -a "${jlName}" "${jDataPath}/transmission /var/db/transmission/ nullfs rw 0 0" 540 | sudo iocage fstab -a "${jlName}" "${jDataPath}/openvpn /usr/local/etc/openvpn/ nullfs rw 0 0" 541 | 542 | # Generic Configuration 543 | pkg_repo 544 | usrpths 545 | jl_init 546 | if [ ! -f "${jDataPath}/transmission/.bash_history" ]; then 547 | sudo touch "${jDataPath}/transmission/.bash_history" 548 | fi 549 | sudo iocage exec -f "${jlName}" -- 'ln -sf "/var/db/transmission/.bash_history" "/root/.bash_history"' 550 | 551 | # Install packages 552 | if ! sudo iocage pkg "${jlName}" install -y openvpn base64 jq; then 553 | echo "Failed to install packages." >&2 554 | exit 1 555 | fi 556 | if ! sudo iocage pkg "${jlName}" install -y transmission-cli transmission-daemon transmission-utils; then 557 | echo "Failed to install packages." >&2 558 | exit 1 559 | fi 560 | 561 | # Set permissions 562 | sudo iocage exec -f "${jlName}" -- "pw groupmod jailmedia -m transmission" 563 | sudo iocage exec -f "${jlName}" -- "touch /var/log/transmission.log" 564 | sudo iocage exec -f "${jlName}" -- "chown transmission /var/log/transmission.log" 565 | 566 | # Enable Services 567 | ## Transmission config 568 | sudo iocage exec -f "${jlName}" -- 'sysrc transmission_enable="YES"' 569 | sudo iocage exec -f "${jlName}" -- 'sysrc transmission_conf_dir="/var/db/transmission"' 570 | sudo iocage exec -f "${jlName}" -- 'sysrc transmission_download_dir="/mnt/incoming/transmission"' 571 | sudo iocage exec -f "${jlName}" -- 'sysrc transmission_flags="--incomplete-dir /mnt/torrents --logfile /var/log/transmission.log"' 572 | sudo iocage exec -f "${jlName}" -- 'sysrc transmission_watch_dir="/mnt/transmission"' 573 | 574 | ## OpenVPN config 575 | sudo iocage exec -f "${jlName}" -- 'sysrc openvpn_enable="YES"' 576 | sudo iocage exec -f "${jlName}" -- "sysrc openvpn_configfile=\"/usr/local/etc/openvpn/${_transmission_openvpn_configfile}\"" 577 | 578 | ## Network config 579 | sudo iocage exec -f "${jlName}" -- 'sysrc firewall_enable="YES"' 580 | sudo iocage exec -f "${jlName}" -- 'sysrc firewall_script="/mnt/scripts/trans/ipfw.rules"' 581 | ## Static route for local inter-vlan connections 582 | if [ ! -z "${_transmission_static}" ]; then 583 | sudo iocage exec -f "${jlName}" -- 'sysrc static_routes="net1"' 584 | sudo iocage exec -f "${jlName}" -- "sysrc net1=\"-net ${_transmission_static}\"" 585 | fi 586 | 587 | # Start services 588 | sudo iocage exec -f "${jlName}" -- "wget http://ipinfo.io/ip -qO -" 589 | sudo iocage exec -f "${jlName}" -- "service openvpn start" 590 | sudo iocage exec -f "${jlName}" -- "service ipfw start" 591 | sudo iocage exec -f "${jlName}" -- "wget http://ipinfo.io/ip -qO -" 592 | sudo iocage exec -f "${jlName}" -- "service transmission start" 593 | 594 | # Final configuration 595 | sudo iocage exec -f "${jlName}" -- 'transmission-remote --torrent-done-script "/mnt/scripts/trans/torrentPost.sh"' 596 | sudo iocage exec -f "${jlName}" -- '/mnt/scripts/trans/pia-port-forward.sh >> /var/log/pia.log 2>&1' 597 | sudo iocage exec -f "${jlName}" -- "cp -sf /mnt/scripts/trans/transmission.logrotate /usr/local/etc/logrotate.d/transmission" 598 | sudo iocage exec -f "${jlName}" -- "crontab /mnt/scripts/trans/transmission.crontab" 599 | 600 | # Set jail to start at boot. 601 | sudo iocage stop "${jlName}" 602 | sudo iocage set boot="1" "${jlName}" 603 | 604 | # Check MAC Address 605 | sudo iocage get vnet0_mac "${jlName}" 606 | 607 | # Create initial snapshot 608 | sudo iocage snapshot "${jlName}" -n InitialConfiguration 609 | sudo iocage start "${jlName}" 610 | } 611 | elif [ "${jlType}" = "unifi" ]; then 612 | jlName="unifi" 613 | { 614 | 615 | # Create jail 616 | if ! sudo iocage create -b -n "${jlName}" -p "/tmp/pkg.json" -r "${ioRelease}" allow_mount="1" mount_fdescfs="1" mount_procfs="1" allow_mount_procfs="1" enforce_statfs="1" allow_set_hostname="1" priority="1" "${_unifi[@]}"; then 617 | exit 1 618 | fi 619 | 620 | # Set Mounts 621 | comn_mnt_pnts 622 | sudo iocage exec -f "${jlName}" -- 'mkdir -pv "/usr/local/share/java/unifi/"' 623 | 624 | sudo iocage fstab -a "${jlName}" "${jDataPath}/unifi /usr/local/share/java/unifi/ nullfs rw 0 0" 625 | 626 | # Generic Configuration 627 | pkg_repo 628 | usrpths 629 | jl_init 630 | if [ ! -f "${jDataPath}/unifi/.bash_history" ]; then 631 | sudo touch "${jDataPath}/unifi/.bash_history" 632 | fi 633 | sudo iocage exec -f "${jlName}" -- 'ln -sf "/usr/local/share/java/unifi/.bash_history" "/root/.bash_history"' 634 | 635 | # Install packages 636 | sudo iocage pkg "${jlName}" install -y openjdk17 mongodb44 || { echo "Failed to install packages." >&2; exit 1;} 637 | sudo iocage pkg "${jlName}" install -y unifi8 || { echo "Failed to install packages." >&2; exit 1;} 638 | 639 | sudo iocage pkg "${jlName}" lock -y openjdk17 mongodb44 unifi8 640 | 641 | # Enable Services 642 | sudo iocage exec -f "${jlName}" -- 'sysrc unifi_enable="YES"' 643 | 644 | # Set jail to start at boot. 645 | sudo iocage stop "${jlName}" 646 | sudo iocage set boot="1" "${jlName}" 647 | 648 | # Check MAC Address 649 | sudo iocage get vnet0_mac "${jlName}" 650 | 651 | # Create initial snapshot 652 | sudo iocage snapshot "${jlName}" -n InitialConfiguration 653 | sudo iocage start "${jlName}" 654 | echo "unifi8 takes about 90 secs to intially boot." 655 | } 656 | elif [ "${jlType}" = "netdata" ]; then 657 | jlName="netdata" 658 | { 659 | 660 | # Create jail 661 | if ! sudo iocage create -b -n "${jlName}" -p "/tmp/pkg.json" -r "${ioRelease}" allow_set_hostname="1" mount_devfs="1" mount_fdescfs="1" mount_procfs="1" securelevel="-1" allow_sysvipc="1" sysvmsg="inherit" sysvsem="inherit" sysvshm="inherit" allow_mount_devfs="1" allow_mount_procfs="1" priority="1" "${_netdata[@]}"; then 662 | exit 1 663 | fi 664 | 665 | # Set Mounts 666 | comn_mnt_pnts 667 | sudo iocage exec -f "${jlName}" -- 'mkdir -pv "/usr/local/etc/netdata/" "/var/cache/netdata/" "/var/db/netdata/" "/mnt/smartd/"' 668 | 669 | sudo iocage fstab -a "${jlName}" "${jDataPath}/netdata/config /usr/local/etc/netdata/ nullfs rw 0 0" 670 | sudo iocage fstab -a "${jlName}" "${jDataPath}/netdata/cache /var/cache/netdata/ nullfs rw 0 0" 671 | sudo iocage fstab -a "${jlName}" "${jDataPath}/netdata/db /var/db/netdata/ nullfs rw 0 0" 672 | sudo iocage fstab -a "${jlName}" "${jDataPath}/netdata/smartd /mnt/smartd/ nullfs rw 0 0" 673 | 674 | # Generic Configuration 675 | pkg_repo 676 | usrpths 677 | jl_init 678 | if [ ! -f "${jDataPath}/netdata/config/.bash_history" ]; then 679 | sudo touch "${jDataPath}/netdata/config/.bash_history" 680 | fi 681 | sudo iocage exec -f "${jlName}" -- 'ln -sf "/usr/local/etc/netdata/.bash_history" "/root/.bash_history"' 682 | sudo iocage exec -f "${jlName}" -- "cp -sf /mnt/scripts/netdata/netdata.logrotate /usr/local/etc/logrotate.d/netdata" 683 | sudo iocage exec -f "${jlName}" -- "crontab /mnt/scripts/netdata/netdata.crontab" 684 | 685 | # Install packages 686 | sudo iocage pkg "${jlName}" install -y netdata netdata-go smartmontools || { echo "Failed to install packages." >&2; exit 1;} 687 | 688 | # Enable Services 689 | sudo iocage exec -f "${jlName}" -- 'sysrc netdata_enable="YES"' 690 | sudo iocage exec -f "${jlName}" -- "service netdata start" 691 | 692 | # Set jail to start at boot. 693 | sudo iocage stop "${jlName}" 694 | sudo iocage set boot="1" "${jlName}" 695 | 696 | # Check MAC Address 697 | sudo iocage get vnet0_mac "${jlName}" 698 | 699 | # Create initial snapshot 700 | sudo iocage snapshot "${jlName}" -n InitialConfiguration 701 | sudo iocage start "${jlName}" 702 | } 703 | elif [ "${jlType}" = "pvr" ]; then 704 | jlName="pvr" 705 | { 706 | 707 | # Create jail 708 | if ! sudo iocage create -b -n "${jlName}" -p "/tmp/pkg.json" -r "${ioRelease}" allow_mlock="1" allow_set_hostname="1" "${_pvr[@]}"; then 709 | exit 1 710 | fi 711 | 712 | # Set Mounts 713 | comn_mnt_pnts 714 | sudo iocage exec -f "${jlName}" -- 'mkdir -pv "/mnt/torrents/" "/mnt/transmission/" "/usr/local/sonarr/" "/usr/local/radarr/" "/usr/local/jackett/" "/usr/local/bazarr/"' 715 | 716 | sudo iocage fstab -a "${jlName}" "${mediaPth} /media/ nullfs rw 0 0" 717 | sudo iocage fstab -a "${jlName}" "${torntPath} /mnt/torrents/ nullfs rw 0 0" 718 | sudo iocage fstab -a "${jlName}" "${thingPath}/Torrents /mnt/transmission/ nullfs rw 0 0" 719 | sudo iocage fstab -a "${jlName}" "${jDataPath}/sonarr /usr/local/sonarr/ nullfs rw 0 0" 720 | sudo iocage fstab -a "${jlName}" "${jDataPath}/radarr /usr/local/radarr/ nullfs rw 0 0" 721 | sudo iocage fstab -a "${jlName}" "${jDataPath}/jackett /usr/local/jackett/ nullfs rw 0 0" 722 | sudo iocage fstab -a "${jlName}" "${jDataPath}/bazarr /usr/local/bazarr/ nullfs rw 0 0" 723 | 724 | # Generic Configuration 725 | pkg_repo 726 | usrpths 727 | jl_init 728 | if [ ! -f "${jDataPath}/sonarr/.bash_history" ]; then 729 | sudo touch "${jDataPath}/sonarr/.bash_history" 730 | fi 731 | sudo iocage exec -f "${jlName}" -- 'ln -sf "/usr/local/sonarr/.bash_history" "/root/.bash_history"' 732 | 733 | # Install packages 734 | sudo iocage pkg "${jlName}" install -y sonarr jackett radarr bazarr mediainfo ca_root_nss || { echo "Failed to install packages." >&2; exit 1;} 735 | sudo iocage pkg "${jlName}" lock -y jackett 736 | 737 | ### mono fixes (see: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=258709) 738 | sudo iocage pkg "${jlName}" install -y /mnt/scripts/pvr/mono6.8-6.8.0.123.txz /mnt/scripts/pvr/py37-pillow-7.0.0.txz /mnt/scripts/pvr/py37-olefile-0.46.txz || { echo "Failed to install packages." >&2; exit 1;} 739 | sudo iocage pkg "${jlName}" lock -y mono6.8 740 | sudo iocage exec -f "${jlName}" -- 'cert-sync "/mnt/scripts/pvr/ca-root-nss.crt"' 741 | ### 742 | 743 | # Set permissions 744 | sudo iocage exec -f "${jlName}" -- "chown -R jackett:jackett /usr/local/share/jackett/" 745 | sudo iocage exec -f "${jlName}" -- "pw groupmod jailmedia -m sonarr" 746 | sudo iocage exec -f "${jlName}" -- "pw groupmod jailmedia -m radarr" 747 | sudo iocage exec -f "${jlName}" -- "pw groupmod jailmedia -m jackett" 748 | sudo iocage exec -f "${jlName}" -- "pw groupmod jailmedia -m bazarr" 749 | 750 | 751 | # Enable Services 752 | sudo iocage exec -f "${jlName}" -- 'sysrc sonarr_enable="YES"' 753 | sudo iocage exec -f "${jlName}" -- 'sysrc radarr_enable="YES"' 754 | sudo iocage exec -f "${jlName}" -- 'sysrc jackett_enable="YES"' 755 | sudo iocage exec -f "${jlName}" -- 'sysrc bazarr_enable="YES"' 756 | 757 | sudo iocage exec -f "${jlName}" -- "service jackett start" 758 | sudo iocage exec -f "${jlName}" -- "service sonarr start" 759 | sudo iocage exec -f "${jlName}" -- "service radarr start" 760 | sudo iocage exec -f "${jlName}" -- "service bazarr start" 761 | 762 | # Set jail to start at boot. 763 | sudo iocage stop "${jlName}" 764 | sudo iocage set boot="1" "${jlName}" 765 | 766 | # Check MAC Address 767 | sudo iocage get vnet0_mac "${jlName}" 768 | 769 | # Create initial snapshot 770 | sudo iocage snapshot "${jlName}" -n InitialConfiguration 771 | sudo iocage start "${jlName}" 772 | } 773 | elif [ "${jlType}" = "flaresolverr" ]; then 774 | jlName="flaresolverr" 775 | { 776 | 777 | # Create jail 778 | if ! sudo iocage create -b -n "${jlName}" -p "/tmp/pkg.json" -r "${ioRelease}" allow_mlock="1" allow_set_hostname="1" "${_flaresolverr[@]}"; then 779 | exit 1 780 | fi 781 | 782 | # Set Mounts 783 | sudo mkdir -pv "${jDataPath}/flaresolverr/home" "${jDataPath}/flaresolverr/app" "${jDataPath}/flaresolverr/undetected_chromedriver" "${jDataPath}/flaresolverr/root/undetected_chromedriver" "${jDataPath}/flaresolverr/share" "${jDataPath}/flaresolverr/git" 784 | sudo chown -R flaresolverr:flaresolverr "${jDataPath}/flaresolverr" 785 | 786 | comn_mnt_pnts 787 | sudo iocage exec -f "${jlName}" -- 'mkdir -pv "/home/flaresolverr" "/app" "/.undetected_chromedriver" "/root/.undetected_chromedriver" "/usr/local/share/flaresolverr" "/mnt/git"' 788 | 789 | sudo iocage fstab -a "${jlName}" "${jDataPath}/flaresolverr/home /home/flaresolverr/ nullfs rw 0 0" 790 | sudo iocage fstab -a "${jlName}" "${jDataPath}/flaresolverr/app /app/ nullfs rw 0 0" 791 | sudo iocage fstab -a "${jlName}" "${jDataPath}/flaresolverr/undetected_chromedriver /.undetected_chromedriver/ nullfs rw 0 0" 792 | sudo iocage fstab -a "${jlName}" "${jDataPath}/flaresolverr/root/undetected_chromedriver /root/.undetected_chromedriver/ nullfs rw 0 0" 793 | sudo iocage fstab -a "${jlName}" "${jDataPath}/flaresolverr/share /usr/local/share/flaresolverr/ nullfs rw 0 0" 794 | sudo iocage fstab -a "${jlName}" "${jDataPath}/flaresolverr/git /mnt/git/ nullfs rw 0 0" 795 | 796 | # Generic Configuration 797 | pkg_repo 798 | usrpths 799 | jl_init 800 | if [ ! -f "${jDataPath}/flaresolverr/home/.bash_history" ]; then 801 | sudo touch "${jDataPath}/flaresolverr/home/.bash_history" 802 | fi 803 | sudo iocage exec -f "${jlName}" -- 'ln -sf "/home/flaresolverr/.bash_history" "/root/.bash_history"' 804 | 805 | # Install packages 806 | sudo iocage pkg "${jlName}" install -y chromium || { echo "Failed to install packages." >&2; exit 1;} 807 | sudo iocage pkg "${jlName}" install -y python39 || { echo "Failed to install packages." >&2; exit 1;} 808 | sudo iocage pkg "${jlName}" install -y xorg-vfbserver || { echo "Failed to install packages." >&2; exit 1;} 809 | sudo iocage pkg "${jlName}" install -y py39-pip py39-virtualenv git || { echo "Failed to install packages." >&2; exit 1;} 810 | 811 | flaresolverr_version="v3.3.16" 812 | if [ ! -d "${jDataPath}/flaresolverr/git/flaresolverr/.git" ]; then 813 | sudo rm -fr "${jDataPath}/flaresolverr/git/flaresolverr" 814 | fi 815 | sudo iocage exec -f "${jlName}" -- "git clone 'https://github.com/FlareSolverr/FlareSolverr.git' '/mnt/git/flaresolverr'" || { echo "Failed to install packages." >&2; exit 1;} 816 | 817 | if [ ! -f "${jDataPath}/flaresolverr/share/.git" ]; then 818 | sudo iocage exec -f "${jlName}" -- "cd '/mnt/git/flaresolverr' && git worktree add '/usr/local/share/flaresolverr' '${flaresolverr_version}'" 819 | else 820 | sudo iocage exec -f "${jlName}" -- "cd '/mnt/git/flaresolverr' && git worktree repair '/usr/local/share/flaresolverr'" || { echo "Failed to install packages." >&2; exit 1;} 821 | sudo iocage exec -f "${jlName}" -- "cd '/usr/local/share/flaresolverr' && git checkout '${flaresolverr_version}'" || { echo "Failed to install packages." >&2; exit 1;} 822 | fi 823 | 824 | # Move things into place 825 | sudo iocage exec -f "${jlName}" -- "cp -a /usr/local/bin/chromedriver /app/chromedriver" 826 | sudo iocage exec -f "${jlName}" -- "cp -a /usr/local/bin/chromedriver /app/chromedriver.exe" 827 | 828 | # Set permissions 829 | sudo iocage exec -f "${jlName}" -- "pw groupadd -n flaresolverr -g 2000" 830 | sudo iocage exec -f "${jlName}" -- "pw useradd -n flaresolverr -g flaresolverr -s '/usr/sbin/nologin' -u 2000 -d '/home/flaresolverr/'" 831 | 832 | sudo iocage exec -f "${jlName}" -- "chown -R flaresolverr:flaresolverr /usr/local/share/flaresolverr/ /home/flaresolverr /app /.undetected_chromedriver root/.undetected_chromedriver" 833 | 834 | sudo iocage exec -f "${jlName}" -- "chmod -R 770 /usr/local/share/flaresolverr" 835 | sudo iocage exec -f "${jlName}" -- "chmod -R 777 /home/flaresolverr /app" 836 | sudo iocage exec -f "${jlName}" -- "chmod -R 775 /.undetected_chromedriver root/.undetected_chromedriver" 837 | 838 | ### virtualenv 839 | sudo iocage exec -f "${jlName}" -- "/mnt/scripts/flaresolverr/build.tool" || { echo "Failed to install packages." >&2; exit 1;} 840 | ### 841 | 842 | # Enable Services 843 | sudo iocage exec -f "${jlName}" -- 'cp -a "/mnt/scripts/flaresolverr/flaresolverr" "/usr/local/etc/rc.d/flaresolverr"' 844 | 845 | sudo iocage exec -f "${jlName}" -- 'sysrc flaresolverr_enable="YES"' 846 | sudo iocage exec -f "${jlName}" -- 'sysrc flaresolverr_daemon_user="root"' 847 | 848 | sudo iocage exec -f "${jlName}" -- "service flaresolverr start" 849 | 850 | # Set jail to start at boot. 851 | sudo iocage stop "${jlName}" 852 | sudo iocage set boot="1" "${jlName}" 853 | 854 | # Check MAC Address 855 | sudo iocage get vnet0_mac "${jlName}" 856 | 857 | # Create initial snapshot 858 | sudo iocage snapshot "${jlName}" -n InitialConfiguration 859 | sudo iocage start "${jlName}" 860 | } 861 | elif [ "${jlType}" = "znc" ]; then 862 | jlName="znc" 863 | { 864 | 865 | # Create jail 866 | if ! sudo iocage create -b -n "${jlName}" -p "/tmp/pkg.json" -r "${ioRelease}" allow_set_hostname="1" priority="2" "${_znc[@]}"; then 867 | exit 1 868 | fi 869 | 870 | # Set Mounts 871 | comn_mnt_pnts 872 | sudo iocage exec -f "${jlName}" -- 'mkdir -pv "/usr/local/etc/znc/"' 873 | 874 | sudo iocage fstab -a "${jlName}" "${jDataPath}/znc/ /usr/local/etc/znc/ nullfs rw 0 0" 875 | 876 | # Generic Configuration 877 | pkg_repo 878 | usrpths 879 | jl_init 880 | if [ ! -f "${jDataPath}/znc/.bash_history" ]; then 881 | sudo touch "${jDataPath}/znc/.bash_history" 882 | fi 883 | sudo iocage exec -f "${jlName}" -- 'ln -sf "/usr/local/etc/znc/.bash_history" "/root/.bash_history"' 884 | 885 | # Install packagespy38-pip 886 | portS 887 | sudo iocage exec -f "${jlName}" -- "portmaster --packages-build --force-config --delete-build-only -db irc/znc" 888 | 889 | # Enable Services 890 | sudo iocage exec -f "${jlName}" -- 'sysrc znc_enable="YES"' 891 | sudo iocage exec -f "${jlName}" -- "service znc start" 892 | 893 | # Set jail to start at boot. 894 | sudo iocage stop "${jlName}" 895 | sudo iocage set boot="1" "${jlName}" 896 | 897 | # Check MAC Address 898 | sudo iocage get vnet0_mac "${jlName}" 899 | 900 | # Create initial snapshot 901 | sudo iocage snapshot "${jlName}" -n InitialConfiguration 902 | sudo iocage start "${jlName}" 903 | } 904 | elif [ "${jlType}" = "gitea" ]; then 905 | jlName="gitea" 906 | { 907 | 908 | # Create jail 909 | if ! sudo iocage create -b -n "${jlName}" -p "/tmp/pkg.json" -r "${ioRelease}" allow_set_hostname="1" priority="99" "${_gitea[@]}"; then 910 | exit 1 911 | fi 912 | 913 | # Set Mounts 914 | comn_mnt_pnts 915 | sudo iocage exec -f "${jlName}" -- 'mkdir -pv "/usr/local/etc/gitea" "/usr/local/share/gitea" "/mnt/repositories" "/usr/local/git"' 916 | 917 | sudo iocage fstab -a "${jlName}" "${jDataPath}/gitea/etc/ /usr/local/etc/gitea/ nullfs rw 0 0" 918 | sudo iocage fstab -a "${jlName}" "${jDataPath}/gitea/share/ /usr/local/share/gitea/ nullfs rw 0 0" 919 | sudo iocage fstab -a "${jlName}" "${gitPath}/ /mnt/repositories/ nullfs rw 0 0" 920 | sudo iocage fstab -a "${jlName}" "$(dirname "${userPth}")/git/ /usr/local/git/ nullfs rw 0 0" 921 | 922 | # Generic Configuration 923 | pkg_repo 924 | usrpths 925 | jl_init 926 | if [ ! -f "${jDataPath}/gitea/etc/.bash_history" ]; then 927 | sudo touch "${jDataPath}/gitea/etc/.bash_history" 928 | fi 929 | sudo iocage exec -f "${jlName}" -- 'ln -sf "/usr/local/etc/gitea/.bash_history" "/root/.bash_history"' 930 | 931 | # Install packages 932 | sudo iocage pkg "${jlName}" install -y gitea git ca_root_nss openssl gnupg || { echo "Failed to install packages." >&2; exit 1;} 933 | sudo iocage pkg "${jlName}" lock -y gitea 934 | 935 | ### Setup gitea 936 | sudo iocage exec -f "${jlName}" -- "openssl rand -base64 64 | tee '/usr/local/etc/gitea/INTERNAL_TOKEN'" 937 | sudo iocage exec -f "${jlName}" -- "openssl rand -base64 32 | tee '/usr/local/etc/gitea/JWT_SECRET'" 938 | ### 939 | 940 | # Set permissions 941 | sudo iocage exec -f "${jlName}" -- "pw groupmod jailmedia -m git" 942 | 943 | # Enable Services 944 | sudo iocage exec -f "${jlName}" -- 'sysrc gitea_enable="YES"' 945 | sudo iocage exec -f "${jlName}" -- "service gitea start" 946 | 947 | # Set jail to start at boot. 948 | sudo iocage stop "${jlName}" 949 | sudo iocage set boot="1" "${jlName}" 950 | 951 | # Check MAC Address 952 | sudo iocage get vnet0_mac "${jlName}" 953 | 954 | # Create initial snapshot 955 | sudo iocage snapshot "${jlName}" -n InitialConfiguration 956 | sudo iocage start "${jlName}" 957 | } 958 | elif [ "${jlType}" = "search" ]; then 959 | jlName="search" 960 | { 961 | 962 | # Create jail 963 | if ! sudo iocage create -b -n "${jlName}" -p "/tmp/pkg.json" -r "${ioRelease}" allow_mount="1" mount_procfs="1" allow_mount_procfs="1" enforce_statfs="1" allow_set_hostname="1" host_hostname="elasticsearch" priority="1" "${_search[@]}"; then 964 | exit 1 965 | fi 966 | 967 | # Set Mounts 968 | comn_mnt_pnts 969 | sudo iocage exec -f "${jlName}" -- 'mkdir -pv "/mnt/fscrawler/" "/usr/local/etc/elasticsearch/" "/var/db/elasticsearch" "/usr/local/etc/kibana" "/mnt/Media/" "/mnt/Things/"' 970 | 971 | sudo iocage fstab -a "${jlName}" "${jDataPath}/fscrawler /mnt/fscrawler/ nullfs rw 0 0" 972 | sudo iocage fstab -a "${jlName}" "${jDataPath}/elasticsearch/etc /usr/local/etc/elasticsearch/ nullfs rw 0 0" 973 | sudo iocage fstab -a "${jlName}" "${jDataPath}/elasticsearch/db /var/db/elasticsearch/ nullfs rw 0 0" 974 | sudo iocage fstab -a "${jlName}" "${jDataPath}/kibana /usr/local/etc/kibana/ nullfs rw 0 0" 975 | sudo iocage fstab -a "${jlName}" "${mediaPth} /mnt/Media/ nullfs ro 0 0" 976 | sudo iocage fstab -a "${jlName}" "${thingPath} /mnt/Things/ nullfs ro 0 0" 977 | 978 | # Generic Configuration 979 | pkg_repo 980 | usrpths 981 | jl_init 982 | if [ ! -f "${jDataPath}/elasticsearch/etc/.bash_history" ]; then 983 | sudo touch "${jDataPath}/elasticsearch/etc/.bash_history" 984 | fi 985 | sudo iocage exec -f "${jlName}" -- 'ln -sf "/usr/local/etc/elasticsearch/.bash_history" "/root/.bash_history"' 986 | 987 | # Install packages 988 | sudo iocage pkg "${jlName}" install -y elasticsearch7 kibana7 tesseract-data tesseract || { echo "Failed to install packages." >&2; exit 1;} 989 | sudo iocage pkg "${jlName}" install -y openjdk17 || { echo "Failed to install packages." >&2; exit 1;} 990 | 991 | ### Setup fscrawler 992 | 993 | ### 994 | 995 | ### Setup elasticsearch 996 | sudo iocage exec -f "${jlName}" -- '/usr/local/lib/elasticsearch/bin/elasticsearch-plugin install --batch ingest-attachment' 997 | ### 998 | 999 | # Set permissions 1000 | sudo iocage exec -f "${jlName}" -- "pw groupmod jailmedia -m elasticsearch" 1001 | 1002 | # Enable Services 1003 | sudo iocage exec -f "${jlName}" -- 'sysrc elasticsearch_enable="YES"' 1004 | sudo iocage exec -f "${jlName}" -- 'sysrc kibana_enable="YES"' 1005 | 1006 | sudo iocage exec -f "${jlName}" -- "service elasticsearch start" 1007 | sudo iocage exec -f "${jlName}" -- "service kibana start" 1008 | 1009 | # Final configuration 1010 | sudo iocage exec -f "${jlName}" -- "crontab /mnt/scripts/search/elasticsearch.crontab" 1011 | 1012 | # Set jail to start at boot. 1013 | sudo iocage stop "${jlName}" 1014 | sudo iocage set boot="1" "${jlName}" 1015 | 1016 | # Check IP Address 1017 | sudo iocage get vnet0_mac "${jlName}" 1018 | 1019 | # Create initial snapshot 1020 | sudo iocage snapshot "${jlName}" -n InitialConfiguration 1021 | sudo iocage start "${jlName}" 1022 | sudo iocage exec -f "${jlName}" -- "/mnt/scripts/search/search.cmd" 1023 | } 1024 | elif [ "${jlType}" = "test" ]; then 1025 | jlName="test" 1026 | { 1027 | 1028 | # Create jail 1029 | if ! sudo iocage create -T -n "${jlName}" -p "/tmp/pkg.json" -r "${ioRelease}" allow_set_hostname="1" "${_test[@]}"; then 1030 | exit 1 1031 | fi 1032 | 1033 | # Set Mounts 1034 | comn_mnt_pnts 1035 | 1036 | # Generic Configuration 1037 | pkg_repo 1038 | usrpths 1039 | jl_init 1040 | 1041 | # Install packages 1042 | sudo iocage pkg "${jlName}" install -y phoronix-test-suite-php74 autoconf automake cmake gmake openjdk8 perl5 pkgconf python python3 || { echo "Failed to install packages." >&2; exit 1;} 1043 | 1044 | # Check MAC Address 1045 | sudo iocage get vnet0_mac "${jlName}" 1046 | 1047 | # Create initial snapshot 1048 | sudo iocage stop "${jlName}" 1049 | sudo iocage snapshot "${jlName}" -n InitialConfiguration 1050 | sudo iocage start "${jlName}" 1051 | } 1052 | elif [ "${jlType}" = "port" ]; then 1053 | jlName="${jlNType}" 1054 | if [ -z "${jlName}" ]; then 1055 | echo "Please specify a jail name." >&2 1056 | exit 1 1057 | fi 1058 | { 1059 | 1060 | # Create jail 1061 | if ! sudo iocage create -T -n "${jlName}" -p "/tmp/pkg.json" -r "${ioRelease}" allow_set_hostname="1" "${_port[@]}"; then 1062 | exit 1 1063 | fi 1064 | 1065 | # Set Mounts 1066 | comn_mnt_pnts 1067 | 1068 | # Generic Configuration 1069 | pkg_repo 1070 | usrpths 1071 | jl_init 1072 | 1073 | # Install packages 1074 | portS 1075 | 1076 | # Check MAC Address 1077 | sudo iocage get vnet0_mac "${jlName}" 1078 | 1079 | # Create initial snapshot 1080 | sudo iocage stop "${jlName}" 1081 | sudo iocage snapshot "${jlName}" -n InitialConfiguration 1082 | sudo iocage start "${jlName}" 1083 | } 1084 | fi 1085 | 1086 | 1087 | ifconfig -a | grep ether 1088 | 1089 | exit 0 1090 | -------------------------------------------------------------------------------- /pia-port-forward.sh: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/bash 2 | 3 | # Copyright (c) 2023 dak180 and contributors. See 4 | # https://opensource.org/licenses/mit-license.php 5 | # 6 | # Enable port forwarding for transmission specifically in FreeBSD. 7 | # 8 | # Requirements: 9 | # This can be executed from cron or from the shell with no arguments 10 | # Ensure that your PIA credentials are accessable from this script. 11 | # 12 | # Packages needed: 13 | # pkg install -y sudo transmission-cli transmission-utils base64 jq curl wget openvpn bash 14 | # 15 | # Usage: 16 | # ./pia-port-forward.sh or bash pia-port-forward.sh or call from cron 17 | # 18 | # shellcheck disable=SC2236 19 | 20 | # Export path for when you use this in cron 21 | export PATH="/sbin:/bin:/usr/sbin:/usr/bin:/usr/games:/usr/local/sbin:/usr/local/bin:/root/bin" 22 | 23 | # Config 24 | vpnUser="transmission" 25 | vpnDir="/usr/local/etc/openvpn" 26 | tempDir="/tmp/piaPort" 27 | curlMaxTime="15" 28 | firewallScript="/mnt/scripts/trans/ipfw.rules" 29 | payloadFile="${tempDir}/payload.sig" 30 | passFile="${vpnDir}/pass.txt" 31 | varFile="${vpnDir}/vars.tool" 32 | confFile="${vpnDir}/openvpn.conf" 33 | gateFile="${tempDir}/gateway.txt" 34 | 35 | mapfile -t auth < "${passFile}" 36 | PIA_USER="${auth[0]}" 37 | PIA_PASS="${auth[1]}" 38 | 39 | if [ ! -d "${tempDir}" ]; then 40 | mkdir -p "${tempDir}" 41 | fi 42 | 43 | function check_for_connectivity() { 44 | if sudo -u "${vpnUser}" -- nc -zw 1 google.com 80 &> /dev/null; then 45 | echo "| VPN connection up." 1>&2 46 | return 0 47 | else 48 | echo "| VPN connection down." 1>&2 49 | return 1 50 | fi 51 | } 52 | 53 | function re_check_connectivity() { 54 | # google.com 55 | if sudo -u "${vpnUser}" -- nc -zw 1 google.com 80 &> /dev/null; then 56 | echo "| VPN connection restored." 1>&2 57 | return 0 58 | else 59 | echo "| Unable to restore VPN connection. Subscription expired? Exiting." 1>&2 60 | exit 1 61 | fi 62 | } 63 | 64 | function restart_vpn() { 65 | 66 | echo "| Restarting openvpn." 1>&2 67 | service openvpn restart &> /dev/null 68 | sleep 15 69 | 70 | ${firewallScript} 71 | } 72 | 73 | function VPN_Status() { 74 | # set adaptorName 75 | # Config 76 | local tunnelAdapter 77 | local try 78 | 79 | tunnelAdapter="$(ifconfig | grep -v "groups" | grep "tun" | cut -d ":" -f1 | tail -n 1)" 80 | while [ -z "${tunnelAdapter}" ] && [ "${try:=0}" -le "20" ]; do 81 | tunnelAdapter="$(ifconfig | grep -v "groups" | grep "tun" | cut -d ":" -f1 | tail -n 1)" 82 | try="$(( try + 1 ))" 83 | sleep 3 84 | done 85 | 86 | if [ -z "${tunnelAdapter}" ]; then 87 | return 1 88 | else 89 | echo "${tunnelAdapter}" 90 | return 0 91 | fi 92 | } 93 | 94 | function is_port_forwarded() { 95 | # test to see if the port is already forwarded 96 | # Config 97 | local json 98 | 99 | # -pt tests for open port. 100 | json="$(transmission-remote -pt 2>&1)" 101 | if [ "${json}" == "Port is open: No" ]; then 102 | echo "| Closed port detected." 1>&2 103 | return 1 104 | elif [ "${json}" == "Port is open: Yes" ]; then 105 | echo "| Open port detected." 1>&2 106 | return 0 107 | else 108 | echo "| Error: transmission said: ${json}" 1>&2 109 | exit 1 110 | fi 111 | } 112 | 113 | function write_gateway_script() { 114 | tee "${varFile}" <<- EOL 115 | #!/usr/local/bin/bash 116 | 117 | 118 | /bin/echo "\${route_vpn_gateway}" > "${gateFile}" 119 | 120 | EOL 121 | chmod +x "${varFile}" 122 | 123 | if grep -q "^script-security 1" < "${confFile}"; then 124 | sed -i '' -e 's:script-security 1:script-security 2:' "${confFile}" 125 | elif grep -q "^script-security 0" < "${confFile}"; then 126 | sed -i '' -e 's:script-security 0:script-security 2:' "${confFile}" 127 | elif ! grep -q "^script-security" < "${confFile}"; then 128 | tee -a "${confFile}" <<< "script-security 2" 129 | fi 130 | tee -a "${confFile}" <<< "up \'${varFile}\'" 131 | restart_vpn 132 | } 133 | 134 | function get_gateway_ip() { 135 | # get gateway ip address 136 | # Config 137 | local gatewayAddress 138 | 139 | if [ ! -x "${varFile}" ]; then 140 | write_gateway_script 141 | elif [ ! -s "${gateFile}" ]; then 142 | restart_vpn 143 | fi 144 | 145 | gatewayAddress="$(cat "${gateFile}")" 146 | 147 | echo "${gatewayAddress}" 148 | return 0 149 | } 150 | 151 | function get_auth_token() { 152 | # Get Auth Token 153 | # Config 154 | local adaptorName 155 | 156 | local authToken 157 | local tokenFile 158 | tokenFile="${payloadFile}" 159 | 160 | adaptorName="${1}" 161 | 162 | if [ -s "${tokenFile}" ]; then 163 | authToken="$(jq -Mre ".payload" < "${tokenFile}" | base64 -d | jq -Mre ".token")" 164 | else 165 | authToken="$(sudo -u "${vpnUser}" -- curl --interface "${adaptorName}" --get --insecure --silent --show-error --fail --location --max-time "${curlMaxTime}" -u "${PIA_USER}:${PIA_PASS}" "https://10.0.0.1/authv3/generateToken" 2> /dev/null | jq -Mre '.token')" 166 | 167 | echo "| Acquiring new auth token." 1>&2 168 | fi 169 | 170 | if [ ! -z "${authToken}" ]; then 171 | echo "${authToken}" 172 | return 0 173 | else 174 | echo "| Failed to acquire new auth token." 1>&2 175 | rm -f "${tokenFile}" 176 | exit 1 177 | fi 178 | } 179 | 180 | function get_payload_and_sig() { 181 | # Get payload & signature 182 | # Config 183 | local authToken 184 | local gatewayAddress 185 | local adaptorName 186 | 187 | local json 188 | local Pstatus 189 | local Pexpire 190 | 191 | authToken="${1}" 192 | gatewayAddress="${2}" 193 | adaptorName="${3}" 194 | 195 | 196 | if [ -s "${payloadFile}" ]; then 197 | json="$(cat "${payloadFile}")" 198 | else 199 | json="$(sudo -u "${vpnUser}" -- curl --interface "${adaptorName}" --get --insecure --silent --show-error --fail --location --max-time "${curlMaxTime}" --data-urlencode "token=${authToken}" "https://${gatewayAddress}:19999/getSignature" 2> /dev/null | jq -Mre .)" 200 | 201 | printf "%s" "${json}" > "${payloadFile}" 202 | echo "| Acquired new Signature." 1>&2 203 | fi 204 | Pstatus="$(echo "${json}" | jq -Mre ".status")" 205 | Pexpire="$(date -juf '%FT%T' "$(echo "${json}" | jq -Mre ".payload" | base64 -d | jq -Mre ".expires_at")" +'%s' 2> /dev/null)" 206 | 207 | if [ ! "${Pstatus}" = "OK" ]; then 208 | echo "| Status is not ok." 1>&2 209 | rm -f "${payloadFile}" 210 | exit 1 211 | elif [ "$(date -ju +'%s' 2> /dev/null)" -ge "${Pexpire}" ]; then 212 | echo "| Payload file is expired." 1>&2 213 | rm -f "${payloadFile}" 214 | exit 1 215 | fi 216 | 217 | echo "${json}" 218 | } 219 | 220 | function set_port() { 221 | # if port is not forwarded, get a new port for transmission 222 | echo "| Loading port forward assignment information.." 1>&2 223 | 224 | # Config 225 | local json 226 | local PORTNUM 227 | 228 | json="${1}" 229 | 230 | PORTNUM="$(echo "${json}" | grep -oE "[0-9]+")" 231 | # test to make sure that the port is actually a number 232 | if echo "${PORTNUM}" | grep -qE '^\-?[0-9]+$'; then 233 | # it IS numeric 234 | echo "| New port: ${PORTNUM}" 1>&2 235 | if ! transmission-remote -p "${PORTNUM}" &> /dev/null; then 236 | return 2 237 | fi 238 | return 0 239 | else 240 | # it is NOT numeric. 241 | echo "| Garbled data: ${PORTNUM}" 1>&2 242 | return 1 243 | fi 244 | } 245 | 246 | function refresh_port() { 247 | # Bind the port to the server; this must be done at least every 15 mins. 248 | 249 | # Config 250 | local payload 251 | local signature 252 | local gatewayAddress 253 | local adaptorName 254 | 255 | local json 256 | local bindStatus 257 | local bindMessage 258 | 259 | payload="${1}" 260 | signature="${2}" 261 | gatewayAddress="${3}" 262 | adaptorName="${4}" 263 | 264 | json="$(sudo -u "${vpnUser}" -- curl --interface "${adaptorName}" --get --insecure --silent --show-error --fail --location --max-time "${curlMaxTime}" --data-urlencode "payload=${payload}" --data-urlencode "signature=${signature}" "https://${gatewayAddress}:19999/bindPort" 2> /dev/null)" 265 | bindStatus="$(echo "${json}" | jq -Mre ".status")" 266 | bindMessage="$(echo "${json}" | jq -Mre ".message")" 267 | 268 | 269 | if [ ! "${bindStatus}" = "OK" ]; then 270 | echo "| Failed to bind the port: ${bindStatus:="Bad Gateway"}; ${bindMessage:="Incorrect gateway address"}" 1>&2 271 | exit 1 272 | else 273 | echo "| Status: ${bindMessage}." 274 | return 0 275 | fi 276 | } 277 | 278 | 279 | # First check for connectivity using the user that executes Transmission. If this fails, script will try to relaunch openvpn service and re-check (15 second pause to allow OpenVPN to start) 280 | # If re-check fails, script will exit without any other execution. 281 | # 282 | # Second this will check for port forward status. If the port forward 283 | # is enabled, script will exit. If the port is not properly 284 | # forwarded, the script will call for a new port assignment and if it 285 | # is valid, will tell transmission to use the new port. 286 | 287 | # echo date/time for logging 288 | echo "+----------------------" 1>&2 289 | echo "| Transmission Port Forward $(date '+%F %T')" 1>&2 290 | 291 | # Check that the vpn is up 292 | if ! check_for_connectivity; then 293 | restart_vpn 294 | tunnelAdapter="$(VPN_Status)" 295 | re_check_connectivity 296 | else 297 | tunnelAdapter="$(VPN_Status)" 298 | fi 299 | 300 | 301 | # Parse Payload 302 | payloadPlusSig="$(get_payload_and_sig "$(get_auth_token "${tunnelAdapter}")" "$(get_gateway_ip)" "${tunnelAdapter}")" 303 | 304 | # Try to catch error conditions 305 | if [ -z "${payloadPlusSig}" ]; then 306 | exit 1 307 | fi 308 | 309 | payloadSig="$(echo "${payloadPlusSig}" | jq -Mre ".signature")" 310 | payLoad="$(echo "${payloadPlusSig}" | jq -Mre ".payload")" 311 | payLoadPort="$(echo "${payLoad}" | base64 -d | jq -Mre ".port")" 312 | 313 | 314 | # Check port 315 | is_port_forwarded 316 | portStatus="${?}" 317 | 318 | # Get the port if we do not have one 319 | if [ "${portStatus}" = "1" ]; then 320 | if ! set_port "${payLoadPort}"; then 321 | echo "| Cannot set the port." 1>&2 322 | exit 1 323 | fi 324 | fi 325 | 326 | # Refresh the port 327 | refresh_port "${payLoad}" "${payloadSig}" "$(get_gateway_ip)" "${tunnelAdapter}" 328 | 329 | exit 0 330 | -------------------------------------------------------------------------------- /search.cmd: -------------------------------------------------------------------------------- 1 | #!/usr/local/bin/bash 2 | # shellcheck disable=SC2010 3 | 4 | 5 | # Config 6 | export JAVA_HOME="/usr/local/openjdk15" 7 | fscrawlerPth="/mnt/fscrawler/fscrawler-es7-2.7-SNAPSHOT/" 8 | fscrawlerJobPth="/mnt/fscrawler/settings" 9 | 10 | readarray -t "fsJobs" <<< "$(ls "${fscrawlerJobPth}" | grep -v "_default" | sed -e 's:/::')" 11 | 12 | 13 | cd "${fscrawlerPth}" || exit 1 14 | 15 | for fsJob in "${fsJobs[@]}"; do 16 | if [ ! -e "/var/run/fscrawler-${fsJob}.pid" ]; then 17 | /usr/sbin/daemon -t "fscrawler-${fsJob}" -P "/var/run/daemon-fscrawler-${fsJob}.pid" -p "/var/run/fscrawler-${fsJob}.pid" -S -u "elasticsearch" nice -20 bin/fscrawler --config_dir "${fscrawlerJobPth}" "${fsJob}" 18 | fi 19 | done 20 | 21 | 22 | exit 0 23 | 24 | -------------------------------------------------------------------------------- /suite-definition.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | FreeNAS-CPU-Stress 6 | 1.0.0 7 | Processor 8 | CPU stress tests for FreeNAS. 9 | dak180 10 | 11 | 12 | pts/build-imagemagick 13 | 14 | 15 | 16 | 17 | 18 | pts/bullet 19 | 3000 fall 20 | Test: 3000 Fall 21 | 22 | 23 | 24 | pts/bullet 25 | 1000 stack 26 | Test: 1000 Stack 27 | 28 | 29 | 30 | pts/bullet 31 | 136 ragdolls 32 | Test: 136 Ragdolls 33 | 34 | 35 | 36 | pts/bullet 37 | 1000 convex 38 | Test: 1000 Convex 39 | 40 | 41 | 42 | pts/bullet 43 | prim-trimesh 44 | Test: Prim Trimesh 45 | 46 | 47 | 48 | pts/bullet 49 | convex-trimesh 50 | Test: Convex Trimesh 51 | 52 | 53 | 54 | pts/bullet 55 | raytests 56 | Test: Raytests 57 | 58 | 59 | 60 | pts/c-ray 61 | 62 | 63 | 64 | 65 | 66 | pts/compress-gzip 67 | 68 | 69 | 70 | 71 | 72 | pts/dcraw 73 | 74 | 75 | 76 | 77 | 78 | pts/encode-mp3 79 | 80 | 81 | 82 | 83 | 84 | pts/fhourstones 85 | 86 | 87 | 88 | 89 | 90 | pts/gnupg 91 | 92 | 93 | 94 | 95 | 96 | pts/himeno 97 | 98 | 99 | 100 | 101 | 102 | pts/java-scimark2 103 | TEST_COMPOSITE 104 | Computational Test: Composite 105 | 106 | 107 | 108 | pts/java-scimark2 109 | TEST_FFT 110 | Computational Test: Fast Fourier Transform 111 | 112 | 113 | 114 | pts/java-scimark2 115 | TEST_SOR 116 | Computational Test: Jacobi Successive Over-Relaxation 117 | 118 | 119 | 120 | pts/java-scimark2 121 | TEST_MONTE 122 | Computational Test: Monte Carlo 123 | 124 | 125 | 126 | pts/java-scimark2 127 | TEST_SPARSE 128 | Computational Test: Sparse Matrix Multiply 129 | 130 | 131 | 132 | pts/java-scimark2 133 | TEST_DENSE 134 | Computational Test: Dense LU Matrix Factorization 135 | 136 | 137 | 138 | pts/john-the-ripper 139 | --format=md5crypt 140 | Test: MD5 141 | 142 | 143 | 144 | pts/john-the-ripper 145 | --format=bcrypt 146 | Test: Blowfish 147 | 148 | 149 | 150 | pts/openssl 151 | 152 | 153 | 154 | 155 | 156 | pts/phpbench 157 | 158 | 159 | 160 | 161 | 162 | pts/pybench 163 | 164 | 165 | 166 | 167 | 168 | pts/sample-program 169 | 170 | 171 | 172 | 173 | 174 | pts/scimark2 175 | TEST_COMPOSITE 176 | Computational Test: Composite 177 | 178 | 179 | 180 | pts/scimark2 181 | TEST_FFT 182 | Computational Test: Fast Fourier Transform 183 | 184 | 185 | 186 | pts/scimark2 187 | TEST_SOR 188 | Computational Test: Jacobi Successive Over-Relaxation 189 | 190 | 191 | 192 | pts/scimark2 193 | TEST_MONTE 194 | Computational Test: Monte Carlo 195 | 196 | 197 | 198 | pts/scimark2 199 | TEST_SPARSE 200 | Computational Test: Sparse Matrix Multiply 201 | 202 | 203 | 204 | pts/scimark2 205 | TEST_DENSE 206 | Computational Test: Dense LU Matrix Factorization 207 | 208 | 209 | 210 | pts/unpack-linux 211 | 212 | 213 | 214 | 215 | 216 | -------------------------------------------------------------------------------- /tlerActiveation.tool: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # shellcheck disable=SC1004,SC2236 3 | 4 | 5 | 6 | 7 | function tler_activation() { 8 | local tlerStatus 9 | 10 | tlerStatus="$(smartctl -jl scterc "/dev/${drive}" | jq -Mre '.ata_sct_erc | values')" 11 | 12 | 13 | if [ ! -z "${tlerStatus}" ]; then 14 | if [ ! "$(echo "${tlerStatus}" | jq -Mre '.read.enabled | values')" = "true" ] || [ ! "$(echo "${tlerStatus}" | jq -Mre '.write.enabled | values')" = "true" ]; then 15 | smartctl -l scterc,70,70 "/dev/${drive}" 16 | fi 17 | fi 18 | 19 | echo "${drive}:" 20 | smartctl -l scterc "/dev/${drive}" | tail -n +4 21 | echo "+---------------+" 22 | } 23 | 24 | function drive_list() { 25 | # Reorders the drives in ascending order 26 | # FixMe: smart support flag is not yet implemented in smartctl json output. 27 | readarray -t "drives" <<< "$(for drive in $(sysctl -n kern.disks | sed -e 's:nvd:nvme:g'); do 28 | if smartctl --json=u -i "/dev/${drive}" | grep "SMART support is:" | grep -q "Enabled"; then 29 | printf "%s " "${drive}" 30 | elif echo "${drive}" | grep -q "nvme"; then 31 | printf "%s " "${drive}" 32 | fi 33 | done | tr ' ' '\n' | sort -V | sed '/^nvme/!H;//p;$!d;g;s:\n::')" 34 | } 35 | 36 | 37 | 38 | 39 | # Check if needed software is installed. 40 | PATH="${PATH}:/usr/local/sbin:/usr/local/bin" 41 | commands=( 42 | sysctl 43 | sed 44 | grep 45 | tr 46 | smartctl 47 | jq 48 | sort 49 | tail 50 | ) 51 | for command in "${commands[@]}"; do 52 | if ! type "${command}" &> /dev/null; then 53 | echo "${command} is missing, please install" >&2 54 | exit 100 55 | fi 56 | done 57 | 58 | 59 | 60 | 61 | drive_list 62 | 63 | for drive in "${drives[@]}"; do 64 | tler_activation 65 | done 66 | --------------------------------------------------------------------------------