diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..9d42190 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,110 @@ +# Common settings that generally should always be used with your language specific settings + +# Auto detect text files and perform LF normalization +* text=auto + +# +# The above will handle all files NOT found below +# + +# Documents +*.bibtex text diff=bibtex +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.epub diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain +*.md text diff=markdown +*.mdx text diff=markdown +*.tex text diff=tex +*.adoc text +*.textile text +*.mustache text +*.tab text +*.tsv text +*.txt text eol=lf +*.sql text +*.csv text eol=crlf + +# Graphics +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.tif binary +*.tiff binary +*.ico binary +*.eps binary +# SVG treated as text by default. +*.svg text +# If you want to treat it as binary, +# use the following line instead. +# *.svg binary + +# Scripts +*.bash text eol=lf +*.fish text eol=lf +*.ksh text eol=lf +*.py text eol=lf +*.php text eol=lf +*.sh text eol=lf +*.zsh text eol=lf +# These are explicitly windows files and should use crlf +*.bat text eol=crlf +*.cmd text eol=crlf +*.ps1 text eol=crlf + +# Visual Dialog Script +*.dsc text eol=crlf +*.dsp text eol=crlf + +# Config files +*.ini text eol=crlf +*.conf text eol=lf + +# Serialisation +*.json text +*.toml text +*.xml text +*.yaml text +*.yml text + +# Archives +*.7z binary +*.bz binary +*.bz2 binary +*.bzip2 binary +*.gz binary +*.lz binary +*.lzma binary +*.rar binary +*.tar binary +*.taz binary +*.tbz binary +*.tbz2 binary +*.tgz binary +*.tlz binary +*.txz binary +*.xz binary +*.Z binary +*.zip binary +*.zst binary + +# Text files where line endings should be preserved +*.patch -text + +# +# Exclude files from exporting +# + +.gitattributes export-ignore +.gitignore export-ignore +.gitkeep export-ignore +.github/ export-ignore +images/ export-ignore diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..f659812 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,3 @@ +github: 007revad +buy_me_a_coffee: 007revad +custom: ["https://www.paypal.me/007revad"] diff --git a/2025_plus_models.md b/2025_plus_models.md new file mode 100644 index 0000000..b4de80b --- /dev/null +++ b/2025_plus_models.md @@ -0,0 +1,136 @@ +## 2025 series or later Plus models + +### UPDATE 7 Oct 2025 + +**DSM 7.3** has removed the many of the restrictions that were in DSM 7.2.2 for 2025 Plus models. + +See https://kb.synology.com/en-global/DSM/tutorial/Drive_compatibility_policies + +The restrictions that remain in DSM 7.3 for 2025 Plus models are: +1. You cannot create a cache or volume on 3rd party NVMe drives. +2. You still see lots of warnings in storage manager when using unverified 3rd party drives. + +Both 1 and 2 can be solved with [Synology HDD db](https://github.com/007revad/Synology_HDD_db) + +
+ +### Unverified 3rd party drive limitations and unofficial solutions for DSM 7.2.2 + +| Action | Works | Result | Solution | +|--------|--------------|--------|----------| +| Setup the NAS with Synology drives | yes | | | +| Setup the NAS with 3rd party SSDs | yes | Lots of warnings | Remove warnings with [Synology HDD db](https://github.com/007revad/Synology_HDD_db) | +| Setup the NAS with unverified 3rd party HDDs | **No!** | | See Setup with unverifed HDDs | +| Migrate unverified 3rd party drives from other Synology | yes | Lots of warnings | Remove warnings with [Synology HDD db](https://github.com/007revad/Synology_HDD_db) | +| Migrate unverified 3rd party drives and NVMe cache from other Synology | yes | Lots of warnings | Remove warnings with [Synology HDD db](https://github.com/007revad/Synology_HDD_db) | +| Replace migrated 3rd party drives with 3rd party drives | **No!** | | Use [Synology HDD db](https://github.com/007revad/Synology_HDD_db) | +| Gradually replace migrated 3rd party drives with Synology drives | yes | Lots of warnings until all 3rd party drives replaced | Use [Synology HDD db](https://github.com/007revad/Synology_HDD_db) See [Migration and Drive Replacement](https://github.com/007revad/Synology_HDD_db/discussions/468#discussioncomment-13086639) | +| Expand migrated 3rd party storage pool with 3rd party drives | **No!** | | Use [Synology HDD db](https://github.com/007revad/Synology_HDD_db) | +| Use 3rd party drive as hot spare | **No!** | | Use [Synology HDD db](https://github.com/007revad/Synology_HDD_db) | +| Create a cache with 3rd party SSDs | **No!** | | Use [Synology HDD db](https://github.com/007revad/Synology_HDD_db) | +| Delete and create storage pool on migrated 3rd party drives | **No!** | | See Recreating storage pool | + +
+ +### Setting up a new 2025 or later plus model with only unverified HDDs in DSM 7.2.2 + +Credit to Alex_of_Chaos on reddit + +DSM won't install on a 2025 or later series plus model if you only have unverified HDDs. But we can get around that. + +1. Start telnet by entering `http://:5000/webman/start_telnet.cgi` into your browser's address bar. + - Replace `` with the IP address of the Synology NAS. +2. Open a telnet client (powershell, PuTTY etc) on your computer and connect to the Synology with 'telnet `. + - Replace `` with the IP address of the Synology NAS. +3. Log into telnet with: + - `root` for the login + - `101-0101` for the password +4. Execute the following command: (using a while loop in case DSM is running in a VM) + ``` + while true; do touch /tmp/installable_check_pass; sleep 1; done + ``` +5. Refresh the web installation page and install DSM. +6. Then in the telnet window, or via SSH, execute the following command: + ``` + /usr/syno/bin/synosetkeyvalue /etc.defaults/synoinfo.conf support_disk_compatibility no + ``` +7. If Storage Manager is already open close then open it, or refresh the web page. If refreshing the page or restarting Storage Manager is not working, try restarting your Synology NAS. +8. You can now create your storage pool from Storage Manager. + +
+ +### Deleting and recreating your storage pool on unverified HDDs in DSM 7.2.2 + +You can't download Synology HDD db to a volume because you've just deleted your storage pool. So you'd first need to download Synology HDD db to a system folder and run it from there. + +You can do this via SSH or via a scheduled task. + +#### Via SSH + +1. Create and cd to /opt + ``` + sudo mkdir /opt && sudo chmod 775 /opt + ``` + +2. Create /opt + ``` + sudo mkdir -m775 /opt + ``` + +2. cd to /opt + ``` + cd /opt || (echo "Failed to CD to /opt"; exit 1) + ``` + +3. Download syno_hdd_db.sh to /opt + ``` + sudo curl -O "https://raw.githubusercontent.com/007revad/Synology_HDD_db/refs/heads/main/syno_hdd_db.sh" + ``` + +4. Download syno_hdd_vendor_ids.txt to /opt + ``` + sudo curl -O "https://raw.githubusercontent.com/007revad/Synology_HDD_db/refs/heads/main/syno_hdd_vendor_ids.txt" + ``` + +5. Then set permissions on /opt/syno_hdd_db.sh + ``` + sudo chmod 750 /opt/syno_hdd_db.sh + ``` + +6. Finally run syno_hdd_db. You don't need any options at this point. + ``` + sudo -s /opt/syno_hdd_db.sh + ``` + +8. If Storage Manager is already open close then open it, or refresh the web page. +9. You can now create your storage pool from Storage Manager. + +#### Via a scheduled task + +First setup email notifications (if you haven't already): + +1. Go to **Control Panel** > **Notification** > **Email** > click **Setup**. + +Then create the scheduled task: + +1. Go to **Control Panel** > **Task Scheduler** > click **Create** > **Scheduled Task** > **User-defined script**. +2. Enter a task name. +3. Select **root** as the user (The script needs to run as root). +4. Untick **Enable**. +5. Click **Task Settings**. +6. Tick **Send run details by email** and enter your email address. +7. In the box under **User-defined script** paste the following: + ``` + mkdir -m775 /opt + cd /opt || (echo "Failed to CD to /opt"; exit 1) + curl -O "https://raw.githubusercontent.com/007revad/Synology_HDD_db/refs/heads/main/syno_hdd_db.sh" + curl -O "https://raw.githubusercontent.com/007revad/Synology_HDD_db/refs/heads/main/syno_hdd_vendor_ids.txt" + chmod 750 /opt/syno_hdd_db.sh + /opt/syno_hdd_db.sh -e + ``` +8. Click **OK** > **OK** > type your password > **Submit** to save the scheduled task. +9. Now select the scheduld task and click **Run** > **OK**. +10. Check your emails to make sure the scheduled task ran without any error. +11. If Storage Manager is already open close then open it, or refresh the web page. +12. You can now create your storage pool from Storage Manager. + diff --git a/CHANGES.txt b/CHANGES.txt index 8af37d1..fe91b64 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,298 @@ +v3.6.124 +- Bug fix for script trying to set write_mostly on SATA M.2 drives. Issue #567 +- Bug fix for script trying to use nvme command to get SATA M.2 drives' vendor id. Issue #567 + +v3.6.123 +- Bug fix for WD Ultrastar drives that have "WDC " before the model name. Issue #559 + +v3.6.122 +- Bug fix for drive models with multiple consecutive spaces in model name. Issue #559 +- Bug fix for nvme drives with multiple consecutive spaces in firmware version. +- Bug fix for options not working. Issue #559 and #563 + +v3.6.121 +- Added --reboot option to reboot the NAS after a DSM update where the build number has changed. + - Only needed if NVMe volume or PCIe card need a 2nd reboot after DSM update. + - Note: This version of the script needs to run once to create the file that tracks the DSM build number. + +v3.6.119 +- Bug fix for "cat: /sys/block//sys/block/sata1/device/vendor: No such file or directory" errors. Issue #554 + +v3.6.118 +- Changed to enable creating M.2 storage pool and volume in Storage Manager after making DSM check disk compatibility. Issue #441 +- Added SSD TRIM warning of possible data loss if non-Synology SSDs or NVMes are in RAID 5, 6 or SHR with more than 3 drives. + - See https://kb.synology.com/en-global/DSM/tutorial/Why_is_SSD_TRIM_available_only_for_SSDs_in_the_compatibility_list + +v3.6.116 +- Moved the "Warn if script located on NVMe volume" code to after the "check for new script version code. Issue #540 +- Bug fix for incorrectly warning that the script is located on an NVMe volume when it is on a SHR HDD volume that has, or had, different HDD sizes. Issue #540 + +v3.6.115 +- Added syno_hdd_shutdown.sh to the auto update code. +- Now if script is located on an NVMe volume it warns you and exits. Previously it just warned you and continued. +- Bug fix for DSM 7.3 not disabling compatible drive database auto update. +- Bug fix for script thinking it's on an NVMe volume if the HDD volume it's on has a NVMe cache. + +v3.6.113 +- Added check that DSM 7.3 db file was successfully compacted. +- Added drive's firmware version to messages. +- Improved checking if drive and firmware version were successfully added to db file. +- Bug fix for adding duplicate drive if 2 or more of the same drive model had different firmware versions. + - Also removes duplicate drive entries if they exist (before correctly editing the file). +- Bug fix adding size_gb to to drive firmware version entries instead of just the "default" key section. + +v3.6.112 +- Bug fix for DSM 7.3 and later where new database files have a space after each : and , + +v3.6.111 +- Added support for M.2 volumes for NVMe drives in PCIe adaptor cards for DSM 7.1.1 + +v3.6.110 +- Bug fix for not finding SAS expansion units. Issue #413 + +v3.6.109 +- Added -I, --ihm option to update IronWolf Health Management to 2.5.1 to support recent model IronWolf and IronWolf Pro drives. + - For NAS with x86_64 CPUs only. + - Installs IronWolf Health Management on '22 series and newer models that don't have IronWolf Health Management (untested). + +v3.5.108 +- Prevent error message if /tmpRoot exists but does not contain /usr/syno/bin/synosetkeyvalue and /etc.defaults/synoinfo.conf. Issue #411 + +v3.5.107 +- Bug fix for firmware version as "Found]" in db files. Issue #407 + +v3.5.106 +- Bug fix for when script is not located on a volume. Issue #397 + +v3.5.105 +- Bug fix for drives that return "HCST " as the model, like HGST drives do. Issue #389 + +v3.5.104 +- Some disks will be blocked in special scenarios. PR #387 + +v3.5.103 +- Bug fix getting size of large drives. Issue #368 + - Run the script once with the --restore option to undue the previous changes then run the script as normal. + +v3.5.102 +- Changed to use a faster method of detecting if an expansion unit is connected, for models with the syno_slot_mapping command. +- Changed so XPE users using the hdddb addon don't need to reboot. + +v3.5.101 +- Changed to support "--restore --ssd=restore" to restore write_mostly when restoring all other changes. Issue #340 + - When using --restore you can also use --ssd=restore, -e or --email + +v3.5.98 +- Changed to automatically download and install dtc if all of the following 4 conditions are met: + 1. dtc is not already installed. + 2. /bin/dtc or dtc are not in the same folder as the script. + 3. The script needs dtc. + 4. The script is running from task scheduler. +- Bug fix for "Enable write_mostly on slow internal drives so DSM runs from the fast internal drive(s)." Issue #340 +- Improved output to make it clear which drive(s) have most_writely set. + +v3.5.97 +- Changed silently skip empty .db.new files with showing an error. Issue #175 +- Changed to warn if db file is 0 bytes. + +v3.5.96 +- Added option to set write_mostly for your internal HDDs so DSM will normally read from your faster internal SSD(s). + - It can automatically set DSM to read from your internal SSDs. + - Or you can tell the script which internal drive(s) DSM should read from. + +Thanks to Xeroxxx for the writemostly suggestion and their writeup here: +https://www.techspark.de/speed-up-synology-dsm-with-hdd-ssd/ + +v3.5.94 +- Updated to support newer drive db versions. + - Synology added size_gb in a recent host v7 version (in SynoOfflinePack 764). + - Synology added barebone_installable_v2 in the latest host v7 version (in SynoOfflinePack 787). +- Bug fix when restoring where memcheck service was only re-enabled on DVA models. +- Changed to add leading 0 to short vids reported by drive. + - Sets 2 vids in case DSM uses the short one (e.g. 0x05dc=brand and 0x5dc=brand). + +Note: You may need to run syno_hdd_db without the -n or --noupdate option, then update the drive database from +"Storage Manager > HDD/SSD > Settings > Advanced > Update Now" then run syno_hdd_db with your preferred options. + +v3.5.93 +- Bug fix for false "Failed to delete tmp files" log entries when script updates itself. Issue #312 + - Bug first appeared in v3.1.64 + +v3.5.92 +- Changed to support M.2 SATA SSDs in M2D18 or M2D17. +- Change -s, --show option to show changes for each different drive model. + +v3.5.91 +- Changed to not exit if no SATA or SAS drives found. Issue #303 + - Now only exits if no SATA, SAS or NVMe drives found. + +v3.5.90 +- Changed to enable creating storage pools/volumes on NVMe drives in a PCIe M.2 adaptor in DSM 7.2 + - Previously only supported DSM 7.2.1 +- Changed to enable creating storage pools/volumes on NVMe drives in a PCIe M.2 adaptor even if PCIe M.2 adaptor not found. + - This may allow creating NVMe volumes on 3rd party PCIe M.2 adaptors. +- Bug fix for when there's multiple expansion unit models only the last expansion unit was processed. Issue #288 +- Bug fix for when there's multiple M2 adaptor card models only the last M2 card was processed. +- Bug fix for incorrectly matching model name variations as well as the exact model name. + - e.g. RX1217 matched RX1217, RX1217rp and RX1217sas. + +v3.5.89 +- Bug fix for -s, --showedits option with multiple of the same drive model but with different firmware versions. Issue #276 + +v3.5.88 +- Changed how memory compatibility is disabled for older models. Issue #272 + +v3.4.87 +- Fix Invalid json format syslog output #270 + +v3.4.86 +- Hard coded /usr/syno/bin/ for Synology commands (to prevent $PATH issues). #249 + +v3.4.84 +- Bug fix when script updates itself and user ran the script from ./scriptname.sh + +v3.4.83 +- Fix for drives that exist in the drive database already but have "compatibility not_support". + - Affected drives were showing as "Incompatible" in storage manager. + - Need to use the -i or --incompatible option. + +v3.4.82 +- Fix for drives that exist in the drive database already but have "compatibility unverified". Issue #224 + - Affected drives were showing as "Unverified" in storage manager. + +v3.4.81 +- Bug fix for false "This script is NOT running on a Synology NAS!" if uname is installed from Entware. Issue #218 + +v3.4.80 +- Bug fix for chmod failed on changes.txt +- Improved help message. + +v3.4.78 +- Added check that script is running on Synology NAS. + +v3.4.77 +- Include smart_test_ignore and smart_attr_ignore in db files. +- Now saves changes.txt as _changes.txt when updating the script. + - To not overwrite changes.txt if my other scripts are in the same folder. +- Bug fix for detecting if script is located on M.2 drive. +- Bug fix for error when -s or --showedits option was used. Issue #200 +- Minor bug fix. + +v3.3.73 +- Bug fix for vendor id. + +v3.3.72 +- Bug fix for enabling creating storage pools in Storage Manager for M.2 drives in PCIe adaptor cards. +- Bug fix for not copying syno_hdd_db_vendors.txt to script location when script updates itself. +- Bug fix for checking if script located on NVMe drive. + +v3.3.70 +- Now enables creating storage pools in Storage Manager for M.2 drives in PCIe adaptor cards: + - E10M20-T1, M2D20, M2D18 and M2D17. + - DSM 7.2.1 and above only. + - Script needs to run after each boot. +- Added new vendor ids for Apacer, aigo, Lexar and Transcend NVMe drives. +- Now includes syno_hdd_vendor.txt so users can add their NVMe drive's vendor id. + - syno_hdd_vendor.txt needs to be in the same folder as syno_hdd_db.sh +- Now warns if script is located on an M.2 volume. + +v3.2.69 +- Added KIOXIA vendor id. +- Bug fix for wrong version number, which caused an update loop. + +v3.2.68 +- Updated so E10M20-T1, M2D20, M2D18 and M2D17 now work in models that use device tree and are using: + - DSM 7.2 Update 2 and 3, 7.2.1, 7.2.1 Update 1, 2 and 3. Issue #132, #148 +- Now edits model.dtb instead of downloading a pre-edited version. +- Improvements to --restore option. +- Fix for Unknown vendor causing "Unsupported firmware version" warning. Issue #161 +- Now supports NVMe drives that show as Unknown brand in storage manager: Issue #161 + - ADATA, Corsair, Gigabyte, HS/MAXIO, MSI, Netac, Phison, PNY + - SK Hynix, Solidigm, SPCC/Lexar, TEAMGROUP, UMIS, ZHITAI +- Fixed bug where memory was shown in MB but with GB unit. +- Removed -i, --immutable option. +- Minor bug fixes. + +v3.1.65 +- Bug fix for NVMe drives with / in the model name for non-device tree Synology models. Issue #154 + +v3.1.64 +- Added -e --email option to disable coloured output to make task scheduler emails easier to read. +- Bug fix for script not updating itself if .sh file had been renamed. +- Bug fix for missing executable permissions if .sh file had been renamed. +- Bug fix to prevent update loop if script's .tar.gz file already exists in /tmp. +- Bug fix to prevent update failing if script's temp folder already exists in /tmp. +- Now only copies CHANGES.txt to script location if script is located on a volume, to prevent putting CHANGES.txt on system partition (/usr/bin, /usr/sbin, /root etc.) + +v3.1.63 +- Added support to disable unsupported memory warnings on DVA models. #136 + +v3.1.62 +- Fixed bug where newly connected expansion units weren't found until up to 24 hours later. Issue #124 + +v3.1.61 +- Added enabling E10M20-T1, M2D20 and M2D18 for DS1821+, DS1621+ and DS1520+. +- Added enabling M2D18 for RS822RP+, RS822+, RS1221RP+ and RS1221+ for older DSM versions. +- Fixed enabling E10M20-T1, M2D20 and M2D18 cards in models that don't officially support them. +- Fixed bugs where the calculated amount of installed memory could be incorrect: + - If last memory socket was empty an invalid unit of bytes could be used. Issue #106 + - When dmidecode returned MB for one ram module and GB for another ram module. Issue #107 +- Fixed bug displaying the max memory setting if total installed memory was less than the max memory. Issue #107 +- Fixed bug where sata1 drive firmware version was wrong if there was a sata10 drive. + +v3.0.56 +- Minor bug fix for checking amount of installed memory. + +v3.0.55 +- Now enables any installed Synology M.2 PCIe cards for models that don't officially support them. + - You can use a M2D20, M2D18, M2D17 or E10M20-T1 on any model with a PCIe slot (not Mini PCIe). +- Now the script reloads itself after updating. +- Added -i, --immutable option to enable immutable snapshots on models older than '20 series running DSM 7.2. +- Added -w, --wdda option to disable WDDA (to prevent warnings when WD drives have been running more than 3 years). +- Added "You may need to reboot" message when NVMe drives were detected. +- Added --autoupdate=[age] option to auto update synology_hdd_db x days after new version released. + - Autoupdate logs update success or errors to DSM system log. +- Changed help to show -r, --ram also sets max memory to the amount of installed memory. +- Changed the "No M.2 cards found" to "No M.2 PCIe cards found" to make it clearer. +- Changed to skip checking the amount of installed memory in DSM 6 (because it was never working in DSM 6). +- Fixed HDD/SSD firmware versions always being 4 characters long (for DSM 7.2 and 6.2.4 Update 7). +- Fixed detecting amount of installed memory (for DSM 7.2 which now reports GB instead of MB). +- Fixed USB drives sometimes being detected as internal drives (for DSM 7.2). +- Fixed error if /run/synostorage/disks/nvme0n1/m2_pool_support doesn't exist yet (for DSM 7.2). +- Fixed drive db update still being disabled in /etc/synoinfo.conf after script run without -n or --noupdate option. +- Fixed drive db update still being disabled in /etc/synoinfo.conf after script run with --restore option. +- Fixed permissions on restored files being incorrect after script run with --restore option. +- Fixed permissions on backup files. + +v2.2.47 +- Updated reboot info in readme. +- Added reboot message for DSM 7. +- Bug fix for issue #77 (escape slashes in drive model name). +- Added error sound. + +v2.2.45 +- Minor bug fix. + +v2.2.44 +- Added --restore info to --help +- Updated restore option to download the latest db files from Synology. +- Now warns you if you try to run it in sh with "sh scriptname.sh" + +v2.2.43 +- Fixed DSM 6 bug where the drives were being duplicated in the .db files each time the script was run. +- Fixed DSM 6 bug where the .db files were being duplicated as .dbr each time the db files were edited. + +v2.2.42 +- Fixed bug where expansion units ending in RP or II were not detected. +- Added a --restore option to undo all changes made by the script. +- Now looks for and edits both v7 and non-v7 db files to solve issue #11 for RS '21 models running DSM 6.2.4. This will also ensure the script still works if: + - Synology append different numbers to the db file names in DSM 8 etc. + - The detected NAS model name does not match the .db files' model name. +- Now backs up the .db.new files (as well as the .db files). +- Now shows max memory in GB instead of MB. +- Now shows status of "Support disk compatibility" setting even if it wasn't changed. +- Now shows status of "Support memory compatibility" setting even if it wasn't changed. + v2.1.38 - Improved shell output when editing max memory setting. - Changed method of checking if drive is a USB drive to prevent ignoring internal drives on RS models. @@ -15,7 +310,7 @@ v2.0.35 - Now allows creating M.2 storage pool and volume all from Storage Manager. v1.3.34 -- Now always shows your drive entries in the host db file if -s or --showedits used instead of only db file was edited during that run. +- Now always shows your drive entries in the host db file if -s or --showedits used instead of only if db file was edited during that run. - Changed to show usage if invalid long option used instead of continuing. v1.3.33 diff --git a/README.md b/README.md index ff31cc3..f7fa3f5 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,23 @@ # Synology HDD db - +![Badge](https://hitscounter.dev/api/hit?url=https%3A%2F%2Fgithub.com%2F007revad%2FSynology_HDD_db&label=Visitors&icon=github&color=%23198754&message=&style=flat&tz=Australia%2FSydney) +[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/paypalme/007revad) [![](https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub&color=%23fe8e86)](https://github.com/sponsors/007revad) + + + + ### Description -Add your SATA or SAS HDDs and SSDs plus SATA and NVMe M.2 drives to your Synology's compatible drive databases, including your M.2 card and Expansion Unit databases. +Add your SATA or SAS HDDs and SSDs plus SATA and NVMe M.2 drives to your Synology's compatible drive databases, including your Synology M.2 PCIe card and Expansion Unit databases. -The script works in DSM 7 and DSM 6. +The script works in DSM 7, including DSM 7.1, 7.2 and 7.3, and DSM 6. + +It also has a restore option to undo all the changes made by the script. + +Also works for 2025 series or later Plus models. See [2025_plus_models.md](https://github.com/007revad/Synology_HDD_db/blob/main/2025_plus_models.md) #### What the script does: @@ -18,75 +27,243 @@ #### What the script does: * Backs up the database files if there is no backup already. * Checks if each drive is already in the Synology's compatible-drive database. * Adds any missing drives to the Synology's compatible-drive database. -* Prevents DSM auto updating the drive database. +* Optionally prevents DSM auto updating the drive database. * Optionally disable DSM's "support_disk_compatibility". -* Optionally disable DSM's "support_memory_compatibility" to prevent non-Synology memory notifications. +* Optionally disable DSM's "support_memory_compatibility" to prevent non-Synology memory notifications. * Optionally edits max supported memory to match the amount of memory installed, if installed memory is greater than the current max memory setting. + * DSM only uses the max memory setting when calculating the reserved RAM area size for SSD caches. +* Optionally set write_mostly for your internal HDDs so DSM will normally read from your faster internal SSD(s). + * It can automatically set DSM to read from your internal SSDs. + * Or you can tell the script which internal drive(s) DSM should read from. +* Optionally disables Western Digital Device Analytics (aka WDDA) to prevent DSM showing a [warning for WD drives that are 3 years old](https://arstechnica.com/gadgets/2023/06/clearly-predatory-western-digital-sparks-panic-anger-for-age-shaming-hdds). + * DSM 7.2.1 already has WDDA disabled. +* Makes DSM allow you to enable SSD TRIM on 3rd party SSDs and NVMes. Don't use the `-f` or --force` option if you want to enable SSD TRIM. + * Do ***NOT*** use TRIM on SSDs in RAID 5, RAID 6 or SHR with 3 or more SSDs unless you are certain your SSDs use TRIM [Method 1 mentioned here](https://kb.synology.com/en-global/DSM/tutorial/Why_is_SSD_TRIM_available_only_for_SSDs_in_the_compatibility_list). + * Enabling TRIM on SSDs that use TRIM [Method 2](https://kb.synology.com/en-global/DSM/tutorial/Why_is_SSD_TRIM_available_only_for_SSDs_in_the_compatibility_list) ***can result in data loss*** in RAID 5, RAID 6 and SHR with 3 or more drives. +* Enables M2D20, M2D18, M2D17 and E10M20-T1 if present on Synology NAS that don't officially support them. + * Newer NAS models may also need [Synology_enable_M2_card](https://github.com/007revad/Synology_enable_M2_card) * Checks that M.2 volume support is enabled (on models that have M.2 slots or PCIe slots). -* Enables creating M.2 storage pools and volumes from within Storage Manager. -* Makes DSM recheck disk compatibility so rebooting is not needed (DSM 7 only). -* Reminds you that you may need to reboot the Synology after running the script (DSM 6 only). +* Enables creating M.2 storage pools and volumes from within Storage Manager in DSM 7.2 and later **(newer models only?)**. + * Including M.2 drives in PCIe adaptor cards like M2D20, M2D18, M2D17 and E10M20-T1 for DSM 7.2 and above **(schedule the script to run boot)**. +* Optionally update IronWolf Health Monitor to v2.5.1 to support recent model IronWolf and IronWolf Pro drives. **(NAS with x86_64 CPUs only)**. + * Also installs IronWolf Health Management on '22 series and newer models that don't have IronWolf Health Management **(untested)**. +* Makes DSM recheck disk compatibility so rebooting is not needed if you don't have M.2 drives (DSM 7 only). + * **If you have M.2 drives you may need to reboot.** + * Reminds you that you may need to reboot the Synology after running the script. * Checks if there is a newer version of this script and offers to download it for you. * The new version available messages time out so they don't prevent the script running if it is scheduled to run unattended. ### Download the script -See How to download the script for the easiest way to download the script. +1. Download the latest version _Source code (zip)_ from https://github.com/007revad/Synology_HDD_db/releases +2. Save the download zip file to a folder on the Synology. + - Do ***NOT*** save the script to a M.2 volume. After a DSM or Storage Manager update the M.2 volume won't be available until after the script has run. +3. Unzip the zip file. + +Or via SSH as your regular user: +``` +cd $HOME +wget https://github.com/007revad/Synology_HDD_db/archive/refs/heads/main.zip -O syno_hdd_db.zip +7z x syno_hdd_db.zip +cd Synology_HDD_db-main && ls -ali +``` + +### Required files + +The following files from the downloaded zip file must be in the same folder: + +1. syno_hdd_db.sh +2. syno_hdd_vendor_ids.txt +3. dtc or the bin folder containing dtc (only required if you have a E10M20-T1, M2D20 or M2D18 in a NAS that does not support them). ### When to run the script -You would need to re-run the script after a DSM update. If you have DSM set to auto update the best option is to run the script every time the Synology boots, and the best way to do that is to setup a scheduled task to run the the script at boot-up. +You would need to re-run the script after a DSM update. If you have DSM set to auto update the best option is to run the script every time the Synology boots, and the best way to do that is to setup a scheduled task to run the the script at boot-up. -**Note:** For DSM 6, after you first run the script you may need to reboot the Synology to see the effect of the changes. +**Note:** After you first run the script you may need to reboot the Synology to see the effect of the changes. -### Options when running the script +### Options when running the script There are optional flags you can use when running the script: -* --showedits or -s to show you the changes it made to the Synology's compatible-drive database. -* --noupdate or -n to prevent DSM updating the compatible drive databases. -* --m2 or -m to prevent processing M.2 drives. -* --force or -f to disable "support_disk_compatibility". This should only be needed if any of your drives weren't detected. - * If you run the script without --force or -f it will re-eanble "support_disk_compatibility". -* --ram or -r to disable "support_memory_compatibility". - * If you run the script without --ram or -r it will re-eanble "support_memory_compatibility". +```YAML + -s, --showedits Show edits made to _host db and db.new file(s) + -n, --noupdate Prevent DSM updating the compatible drive databases + -r, --ram Disable memory compatibility checking (DSM 7.x only) + and sets max memory to the amount of installed memory + -f, --force Force DSM to not check drive compatibility + Do not use this option unless absolutely needed + -i, --incompatible Change incompatible drives to supported + Do not use this option unless absolutely needed + -w, --wdda Disable WD Device Analytics to prevent DSM showing + a false warning for WD drives that are 3 years old + DSM 7.2.1 and later already has WDDA disabled + -p, --pcie Enable creating volumes on M2 in unknown PCIe adaptor + -e, --email Disable colored text in output scheduler emails + -S, --ssd=DRIVE Enable write_mostly on internal HDDs so DSM primarily + reads from internal SSDs or your specified drives + -S automatically sets internal SSDs as DSM preferred + --ssd=DRIVE requires the fast drive(s) as argument, + or restore as the argument to reset drives to default + --ssd=sata1 or --ssd=sata1,sata2 or --ssd=sda etc + --ssd=restore + --restore Undo all changes made by the script (except -S --ssd) + To restore all changes including write_mostly use + --restore --ssd=restore + --autoupdate=AGE Auto update script (useful when script is scheduled) + AGE is how many days old a release must be before + auto-updating. AGE must be a number: 0 or greater + -I, --ihm Update IronWolf Health Management to 2.5.1 to support + recent model IronWolf and IronWolf Pro drives. + For NAS with x86_64 CPUs only. + Also installs IHM on '22 series and newer models (untested) + --reboot Reboot after a DSM update when build number has changed + Only works if script is running from a boot-up schedule + Only needed if NVMe volume or PCIe card need a 2nd + reboot after DSM update + -h, --help Show this help message + -v, --version Show the script version +``` -

+**Notes:** +- The -f or --force option is only needed if for some reason your drives still show as unsupported in storage manager. + - Only use this option as last resort. + - Using this option will prevent data deduplication from being available, and prevent firmware updates on Synology brand drives. +- If you have some Synology drives and want to update their firmware run the script **without** --noupdate or -n then do the drive database update from Storage Manager and finally run the script again with your preferred options. ### Scheduling the script in Synology's Task Scheduler -See How to schedule a script in Synology Task Manager +See How to schedule a script in Synology Task Scheduler ### Running the script via SSH -You run the script in a shell with sudo -i or as root. +[How to enable SSH and login to DSM via SSH](https://kb.synology.com/en-global/DSM/tutorial/How_to_login_to_DSM_with_root_permission_via_SSH_Telnet) + +You run the script in a shell with sudo -s or as root. ```YAML -sudo -i /path-to-script/syno_hdd_db.sh -nr +sudo -s /path-to-script/syno_hdd_db.sh -nr ``` **Note:** Replace /path-to-script/ with the actual path to the script on your Synology. -

+

-If you run the script with the -showedits flag it will show you the changes it made to the Synology's compatible-drive database. Obviously this is only useful if you run the script in a shell. +If you run the script with the --showedits flag it will show you the changes it made to the Synology's compatible-drive database. Obviously this is only useful if you run the script in a shell. ```YAML -sudo -i /path-to-script/syno_hdd_db.sh -nr -showedits +sudo -s /path-to-script/syno_hdd_db.sh -nr --showedits ``` **Note:** Replace /path-to-script/ with the actual path to the script on your Synology. -

+

+ +### Troubleshooting + +| Issue | Cause | Solution | +|-------|-------|----------| +| /usr/bin/env: ‘bash\r’: No such file or directory | File has Mac line endings! | [Download latest zip file](https://github.com/007revad/Synology_HDD_db/releases) | +| Cursor sits there doing nothing | File has Windows line endings! | [Download latest zip file](https://github.com/007revad/Synology_HDD_db/releases) | +| syntax error near unexpected token | You downloaded the webpage! | [Download latest zip file](https://github.com/007revad/Synology_HDD_db/releases) | + +If you get a "No such file or directory" error check the following: + +1. Make sure you downloaded the zip or rar file to a folder on your Synology (not on your computer). +2. Make sure you unpacked the zip or rar file that you downloaded and are trying to run the syno_hdd_db.sh file. +3. If the path to the script contains any spaces you need to enclose the path/scriptname in double quotes: + ```YAML + sudo -s "/volume1/my scripts/syno_hdd_db.sh -n" + ``` +4. Set the script file as executable: + ```YAML + sudo chmod +x "/volume1/scripts/syno_hdd_db.sh" + ``` + + +### vendor_ids.txt + +You only need to edit syno_hdd_vendor_ids.txt if the script warns you about a missing vendor id. + +If DSM doesn't know the brand of your NVMe drives they will show up in Storage Manager as Unknown brand, and Unrecognised firmware version. + +

+ +In this case the script will show you the vendor ID and advise you to add it to the syno_hdd_vendor_ids.txt file. + +

+ +### Ironwolf Health + +Ironwolf Health working with the latest version of Ironwolf Health Monitor. +

+ +
-**Credits** +### Credits - The idea for this script came from a comment made by Empyrealist on the Synology subreddit. - Thanks for the assistance from Alex_of_Chaos on the Synology subreddit. - Thanks to dwabraxus and aferende for help detecting connected expansion units. -- Thanks to bartoque on the Synology subreddit for the tip on making the script download the latest release from github. +- Thanks to bartoque on the Synology subreddit for the tip on making the script download the latest release from GitHub. - Thanks to nicolerenee for pointing out the easiest way to enable creating M.2 storage pools and volumes in Storage Manager. +- Thanks to Xeroxxx for the writemostly suggestion and their writeup here: https://www.techspark.de/speed-up-synology-dsm-with-hdd-ssd/ -**Donators** +### Donators -- A big thank you to Gummibando on reddit for their donation. +Thank you to the PayPal and Buy Me a Coffee donators, GitHub sponsors and hardware donators +| | | | | +|--------------------|--------------------|----------------------|----------------------| +| | | | David Koch | +| EVOX S.R.L.S | Adam | Danny Schoonderwoert | Andrew Ward | +| nickcolea | Bruno | eColombel | LordGardenGnome | +| Dennis Struck | matthewslack | Adrien | Florian Hinhamer | +| Patrick Witon | Sonwoa1 | apbirch67 | Tim | +| Robert | Moritz Bloser | Jason Huang | James Welsh | +| robinhood1995 | Paul Fiorento | Gwystyl | Matt Hann | +| Robert Šega | Marco Brenke | Martinus Humblet | Dirk Köhler | +| KryoFlux GmbH | Frederic Gobry | bizIT Hirschberg | Alexander Ziemann | +| Chris Black Media | Brent Bertram | Carsten Schmidt | Christopher Nichols | +| Roland Thätig | Sebastian Brandt | HFB2022 | jtrouzes | +| stove | Simon Küest | Oliver Weber | Kevin Randino | +| Alex Tripp | Peter Kleissner | Dominic Lee | Daniel Boecker | +| Pat A Phillips | Craikeybaby | Jason DeCorte | Salovaara Antti Sakari | +| Jérôme MORIN | Sven Bauer | Fabien Vallet | Fabio Petgola | +| lonestar6262 | Netchoice | Fabio Cecchinato | Jacek | +| Dugan Audio LLC | MikeSx | Toregev | M. Verhoef | +| Philipp Ehmeier | Adrian Playle | Daniel Meda | Richard Wilhelm | +| Mika255 | Ralf Edelwein | Martin | Alexander Habisreitinger | +| jrn | Marcus Wojtusik | Will (war59312) | Christopher Maglio | +| Flow | Jake Morrison | tsnyder | zhangchi198 | +| leadadri | Gary Plumbridge | frogger1805 | ctrlaltdelete | +| CannotTouch | Kevin Staude | Alistair Hathaway | 8347 | +| BrattishPlaque | Chris Bunnell | dansimau | Bsih | +| Tim Trace | Michel VIEUX-PERNON | R De Jong | Rick | +| Klaus-Dieter Fahlbusch | Amarand Agasi | someone61986 | Alexander Machatschek | +| Yeong​Nuno | Joe | Torben Schreiter | Anthony McMurray | +| Abhishek | Steven Haskell | Malte Müller | Aaron Thomas | +| DENNIS BRAZIL | kunvanimals | Arnaud Costermans | dealchecker | +| Michael Carras | Alan | speedyyyyyy | Jordi Chavarria Fibla | +| Qwerty.xyz | Max | Mark Rohde | Someone | +| vaadmin | Sebastiaan Mulder | Nico Stark | Oleksandr Antonishak | +| Marcel Siemienowski | Dave Smart | dweagle79 | lingyinsam | +| Vojtech Filkorn | Craig Sadler | Po-Chia Chen | Jean-François Fruhauf | +| Sven 'ctraltdelete' | Thomas Horn | Christian | Simon Azzouni | +| Lee Booy | Bünyamin Olgun | Hartmut Heinbach | Alexander Gundermann | +| Björn Schöninger | Nico Scherer | Patrick Hoekstra | Alex Joyce | +| Marcus Ackermann | Lorenz Schmid | enil-kil | Xaver Zöllner | +| Jan Bublitz | Darren O'Connor | Charles Young | J Davis | +| Jürg Baiker | Joshua Gillispie | bizIT Hirschberg | Jordan Crawford | +| Tyler Teal | Voluntary Commerce LLC | Ez Hosting | Alec Wilhere | +| Reece Lyne | Enric Escudé Santana | Yunhao Zhang | Matthias Gerhardt | +| Darryl Harper | Mikescher | Matthias Pfaff | cpharada | +| Neil Tapp | zen1605 | Kleissner Investments | Angel Scandinavia | +| B Collins | Peter jackson | Mir Hekmat | Andrew Tapp | +| Peter Weißflog | Joseph Skup | Dirk Kurfuerst | Gareth Locke | +| Rory de Ruijter | Nathan O'Farrell | Harry Bos | Mark-Philipp Wolfger | +| Filip Kraus | John Pham | Alejandro Bribian Rix | Daniel Hofer | +| Bogdan-Stefan Rotariu | Kevin Boatswain | anschluss-org | Yemeth | +| Patrick Thomas | Manuel Marquez Corral | Evrard Franck | Chad Palmer | +| 侯​永政 | CHEN​HAN-YING | Eric Wells | Massimiliano Pesce | +| JasonEMartin | Gerrit Klussmann | Alain Aube | Robert Kraut | +| Charles-Edouard Poisnel | Oliver Busch | anonymous donors | private sponsors | diff --git a/bin/dhm_tool b/bin/dhm_tool new file mode 100644 index 0000000..3ce8e45 Binary files /dev/null and b/bin/dhm_tool differ diff --git a/bin/dhm_tool.txt b/bin/dhm_tool.txt new file mode 100644 index 0000000..e07189f --- /dev/null +++ b/bin/dhm_tool.txt @@ -0,0 +1,12 @@ +stx_ihm from QNAP firmware, renamed dhm_tool for Synology DSM + + SeaDragon_DHMr - Seagate drive utilities + Copyright (c) 2017-2021 Seagate Technology LLC and/or its Affiliates, All Rights Reserved + SeaDragon_DHMr Version: 2.5.1-2_2_3 X86_64 + Build Date: Oct 3 2022 + +MD5 cf67c1d5006913297f85ca7f9d1795ba +SHA-1 163b384afabc491c97f3aa4ee7916601a9a680dd +SHA-256 86dd0e3c5ded507d23dfeb2ca04a2e086c97a86e2ab96dc6fdeab686a8202795 +SHA-384 3d806909d445605787ceb155bc1555e01d02efe324378c9c71e6b53d91bba6bf8dbadef38dd882b43caf8dbd93eaff6c +SHA-512 a2d87d91fd4cc298708946c9a611027dce290ba17a48c65e712b5d7e72ba04c6b5303c90efeb178a8fc690241b405691a15cc88200ab3b28319198df6bd6ad16 diff --git a/bin/dtc b/bin/dtc new file mode 100644 index 0000000..d3da15b Binary files /dev/null and b/bin/dtc differ diff --git a/bin/dtc_info.txt b/bin/dtc_info.txt new file mode 100644 index 0000000..b506120 --- /dev/null +++ b/bin/dtc_info.txt @@ -0,0 +1,8 @@ +MD5 01381dabbe86e13a2f4a8017b5552918 +SHA-1 f24a4c06b4865a5948eddc2f43fcdb7700398c7d +SHA-256 14f7f87a5822050eab7c30a064912a6efe99f49bf6b896bbb38c2ba003803e78 +Vhash 4885c57da971db5c59998566ee02855f +SSDEEP 1536:ZJ048w/2Iaosz0SlWcCyeyudm5gupvatDVF4Utc3Lyk+pboAZ4NMMy6MajiRTrbc:0hfosz0j0ehm57vaTDc3Lmo/iRTrM4q +TLSH T13EE33D02F3648C7DD4F85B34469A82B7A7B4F8455232171F7B4097192F86F688F2A9E3 + +https://www.virustotal.com/gui/file/14f7f87a5822050eab7c30a064912a6efe99f49bf6b896bbb38c2ba003803e78/detection diff --git a/bin/dtc_source.txt b/bin/dtc_source.txt new file mode 100644 index 0000000..c1a744b --- /dev/null +++ b/bin/dtc_source.txt @@ -0,0 +1,2 @@ +dtc-1.6.1 source code - Author David Gibson +https://git.kernel.org/pub/scm/utils/dtc/dtc.git diff --git a/images/ihm.png b/images/ihm.png new file mode 100644 index 0000000..cfb85e7 Binary files /dev/null and b/images/ihm.png differ diff --git a/images/schedule3-2.png b/images/schedule3-2.png new file mode 100644 index 0000000..d9df547 Binary files /dev/null and b/images/schedule3-2.png differ diff --git a/images/syno_hdd_db_help2.png b/images/syno_hdd_db_help2.png new file mode 100644 index 0000000..a05aab8 Binary files /dev/null and b/images/syno_hdd_db_help2.png differ diff --git a/images/unknown.png b/images/unknown.png new file mode 100644 index 0000000..11f4fb6 Binary files /dev/null and b/images/unknown.png differ diff --git a/images/update-now-disabled.png b/images/update-now-disabled.png new file mode 100644 index 0000000..9619bdb Binary files /dev/null and b/images/update-now-disabled.png differ diff --git a/images/update-now-working.png b/images/update-now-working.png new file mode 100644 index 0000000..1b9fbb6 Binary files /dev/null and b/images/update-now-working.png differ diff --git a/images/vendor_ids.png b/images/vendor_ids.png new file mode 100644 index 0000000..a0440f3 Binary files /dev/null and b/images/vendor_ids.png differ diff --git a/my-other-scripts.md b/my-other-scripts.md new file mode 100644 index 0000000..9466f6f --- /dev/null +++ b/my-other-scripts.md @@ -0,0 +1,183 @@ +## All my scripts, tools and guides + + + + +#### Contents +- [Plex](#plex) +- [Synology docker](#synology-docker) +- [Synology recovery](#synology-recovery) +- [Other Synology scripts](#other-synology-scripts) +- [Synology hardware restrictions](#synology-hardware-restrictions) +- [2025 plus models](#2025-plus-models) +- [How To Guides](#how-to-guides) +- [Synology dev](#synology-dev) + +*** + +### Plex + +- **Synology_Plex_Backup** + - A script to backup Plex to a tgz file foror DSM 7 and DSM 6. + - Works for Plex Synology package and Plex in docker. + +- **Asustor_Plex_Backup** + - Backup your Asustor's Plex Media Server settings and database. + +- **Linux_Plex_Backup** + - Backup your Linux Plex Media Server's settings and database. + +- **Plex_Server_Sync** + - Sync your main Plex server database & metadata to a backup Plex server. + - Works for Synology, Asustor, Linux and supports Plex package or Plex in docker. + +               [Back to Contents](#contents) + +### Synology docker + +- **Synology_Docker_export** + - Export all Synology Container Manager or Docker containers' settings as json files to your docker shared folder. + +- **Synology_ContainerManager_IPv6** + - Enable IPv6 for Container Manager's bridge network. + +- **ContainerManager_for_all_armv8** + - Script to install Container Manager on a RS819, DS119j, DS418, DS418j, DS218, DS218play or DS118. + +- **Docker_Autocompose** + - Create .yml files from your docker existing containers. + +- **Synology_docker_cleanup** + - Remove orphan docker btrfs subvolumes and images in Synology DSM 7 and DSM 6. + +               [Back to Contents](#contents) + +### Synology recovery + +- **Synology_DSM_reinstall** + - Easily re-install the same DSM version without losing any data or settings. + +- **Synology_Recover_Data** + - A script to make it easy to recover your data from your Synology's drives using a computer. + +- **Synology clear drive error** + - Clear drive critical errors so DSM will let you use the drive. + +- **Synology_DSM_Telnet_Password** + - Synology DSM Recovery Telnet Password of the Day generator. + +- **Syno_DSM_Extractor_GUI** + - Windows GUI for extracting Synology DSM 7 pat files and spk package files. + +- **Synoboot_backup** + - Back up synoboot after each DSM update so you can recover from a corrupt USBDOM. + +               [Back to Contents](#contents) + +### Other Synology scripts + +- **Synology_app_mover** + - Easily move Synology packages from one volume to another volume. + +- **Video_Station_for_DSM_722** + - Script to install Video Station in DSM 7.2.2 + +- **SS_Motion_Detection** + - Installs previous Surveillance Station and Advanced Media Extensions versions so motion detection and HEVC are supported. + +- **Synology_Config_Backup** + - Backup and export your Synology DSM configuration. + +- **Synology_CPU_temperature** + - Get and log Synology NAS CPU temperature via SSH. + +- **Synology_SMART_info** + - Show Synology smart test progress or smart health and attributes. + +- **Synology_Cleanup_Coredumps** + - Cleanup memory core dumps from crashed processes. + +- **Synology_toggle_reset_button** + - Script to disable or enable the reset button and show current setting. + +- **Synology_Download_Station_Chrome_Extension** + - Download Station Chrome Extension. + +- **Seagate_lowCurrentSpinup** + - This script avoids the need to buy and install a higher wattage power supply when using multiple large Seagate SATA HDDs. + +- **Synology_created_date** + - Script to show you the created year and month of your Synology NAS or expansion unit. + +               [Back to Contents](#contents) + +### Synology hardware restrictions + +- **Synology_HDD_db** + - Add your SATA or SAS HDDs and SSDs plus SATA and NVMe M.2 drives to your Synology's compatible drive databases, including your Synology M.2 PCIe card and Expansion Unit databases. + +- **Synology_enable_M2_volume** + - Enable creating volumes with non-Synology M.2 drives. + - Enable Health Info for non-Synology NVMe drives (not in DSM 7.2.1 or later). + +- **Synology_M2_volume** + - Easily create an M.2 volume on Synology NAS. + +- **Synology_enable_M2_card** + - Enable Synology M.2 PCIe cards in Synology NAS that don't officially support them. + +- **Synology_enable_eunit** + - Enable an unsupported Synology eSATA Expansion Unit models. + +- **Synology_enable_Deduplication** + - Enable deduplication with non-Synology SSDs and unsupported NAS models. + +- **Synology_SHR_switch** + - Easily switch between SHR and RAID Groups, or enable RAID F1. + +- **Synology_enable_sequential_IO** + - Enables sequential I/O for your SSD caches, like DSM 6 had. + +- **Synology_Information_Wiki** + - Information about Synology hardware. + +               [Back to Contents](#contents) + +### 2025 plus models + +- **Transcode_for_x25** + - Installs the modules needed for Plex or Jellyfin hardware transcoding in DS425+ and DS225+. + +- **2025 series or later Plus models** + - Unverified 3rd party drive limitations and unofficial solutions. + +- **Setup with only 3rd party drives** + - Setting up a new 2025 or later plus model with only unverified HDDs. + +- **Recreating storage pool on migrated drives** + - Deleting and recreating your storage pool on unverified HDDs. + +               [Back to Contents](#contents) + +### How To Guides + +- **Synology_SSH_key_setup** + - How to setup SSH key authentication for your Synology. + +               [Back to Contents](#contents) + +### Synology dev + +- **Download_Synology_Archive** + - Download all or part of the Synology archive. + +- **Syno_DSM_Extractor_GUI** + - Windows GUI for extracting Synology DSM 7 pat files and spk package files. + +- **ScriptNotify** + - DSM 7 package to allow your scripts to send DSM notifications. + +- **DTC_GUI_for_Windows** + - GUI for DTC.exe for Windows. + +               [Back to Contents](#contents) diff --git a/syno_hdd_db.sh b/syno_hdd_db.sh index 8197c5a..14086d3 100644 --- a/syno_hdd_db.sh +++ b/syno_hdd_db.sh @@ -1,166 +1,108 @@ #!/usr/bin/env bash -# shellcheck disable=SC1083,SC2054,SC2121 +# shellcheck disable=SC1083,SC2054,SC2121,SC2207 #-------------------------------------------------------------------------------------------------- # Github: https://github.com/007revad/Synology_HDD_db # Script verified at https://www.shellcheck.net/ -# Tested on DSM 7.2 beta, 7.1.1 and DSM 6.2.4 -# -# Easiest solution: -# Edit /etc.defaults/synoinfo.conf and change support_disk_compatibility="yes" to "no" and reboot. -# Then all drives can be used without error messages. -# -# But lets do this the proper way by adding our drive models to the appropriate .db file. # # To run in task manager as root (manually or scheduled): # /volume1/scripts/syno_hdd_db.sh # replace /volume1/scripts/ with path to script # # To run in a shell (replace /volume1/scripts/ with path to script): -# sudo /volume1/scripts/syno_hdd_db.sh +# sudo -i /volume1/scripts/syno_hdd_db.sh # or -# sudo /volume1/scripts/syno_hdd_db.sh -showedits +# sudo -i /volume1/scripts/syno_hdd_db.sh -showedits # or -# sudo /volume1/scripts/syno_hdd_db.sh -force -showedits +# sudo -i /volume1/scripts/syno_hdd_db.sh -force -showedits #-------------------------------------------------------------------------------------------------- +# https://smarthdd.com/database/ -# TODO -# Bypass M.2 volume lock for unsupported M.2 drives. -# See https://github.com/007revad/Synology_enable_M2_volume -# -# Maybe also edit the other disk compatibility db in synoboot, used during boot time. -# It's also parsed and checked and probably in some cases it could be more critical to patch that one instead. +# RECENT CHANGES +# Make DSM read md0 and md1 from SSD drive(s) if internal SSD and HDD are installed. +# https://github.com/007revad/Synology_HDD_db/issues/318 +# https://www.techspark.de/speed-up-synology-dsm-with-hdd-ssd/ +# https://raid.wiki.kernel.org/index.php/Write-mostly -# DONE -# Improved shell output when editing max memory setting. -# -# Changed method of checking if drive is a USB drive to prevent ignoring internal drives on RS models. -# -# Changed to not run "synostgdisk --check-all-disks-compatibility" in DSM 6.2.3 (which has no synostgdisk). -# -# Now edits max supported memory to match the amount of memory installed, if greater than the current max memory setting. -# -# Now allows creating M.2 storage pool and volume all from Storage Manager -# -# Now always shows your drive entries in the host db file if -s or --showedits used, -# instead of only db file was edited during that run. -# -# Changed to show usage if invalid long option used instead of continuing. -# -# Fixed bug inserting firmware version for already existing model. -# -# Changed to add drives' firmware version to the db files (to support data deduplication). -# See https://github.com/007revad/Synology_enable_Deduplication -# -# Changed to be able to edit existing drive entries in the db files to add the firmware version. -# -# Now supports editing db files that don't currently have any drives listed. -# -# Fixed bug where the --noupdate option was coded as --nodbupdate. Now either will work. -# -# Fixed bug in re-enable drive db updates -# -# Fixed "download new version" failing if script was run via symlink or ./ -# -# Changed to show if no M.2 cards were found, if M.2 drives were found. -# -# Changed latest version check to download to /tmp and extract files to the script's location, -# replacing the existing .sh and readme.txt files. -# -# Added a timeouts when checking for newer script version in case github is down or slow. -# -# Added option to disable incompatible memory notifications. -# -# Now finds your expansion units' model numbers and adds your drives to their db files. -# -# Now adds your M.2 drives to your M.2 PCI cards db files (M2Dxx and E10M20-T1 and future models). -# -# Improved flags/options checking and added usage help. -# -# Can now download the latest script version for you (if you have user home service enabled in DSM). -# -# Now adds 'support_m2_pool="yes"' line for models that don't have support_m2_pool in synoinfo.conf -# to (hopefully) prevent losing your SSH created M2 volume when running this script on models -# that DSM 7.2 Beta does not list as supported for creating M2 volumes. -# -# Changed Synology model detection to be more reliable (for models that came in different variations). -# -# Changed checking drive_db_test_url setting to be more durable. -# -# Added removal of " 00Y" from end of Samsung/Lenovo SSDs to fix issue #13. -# -# Fixed bug where removable drives were being detected and added to drive database. -# -# Fixed bug where "M.2 volume support already enabled" message appeared when NAS had no M.2 drives. -# -# Added check that M.2 volume support is enabled (on supported models). -# -# Added support for M.2 SATA drives. -# -# Can now skip processing M.2 drives by running script with the -m2 flag. -# -# Changed method of getting drive and firmware version so script is faster and easier to maintain. -# - No longer using smartctl or hdparm. -# -# Changed SAS drive firmware version detection to support SAS drives that hdparm doesn't work with. -# -# Removed error message and aborting if *.db.new not found (clean DSM installs don't have a *.db.new). -# -# Force DSM to check disk compatibility so reboot not needed (DSM 6 may still need a reboot). -# -# Fixed DSM 6 issue when DSM 6 has the old db file format. -# -# Add support for SAS drives. -# -# Get HDD/SSD/SAS drive model number with smartctl instead of hdparm. -# -# Check if there is a newer script version available. -# -# Add support for NVMe drives. -# -# Prevent DSM auto updating the drive database. -# -# Optionally disable "support_disk_compatibility". +# TODO +# Enable SMART Attributes button on Storage Manager +# disabled:e.healthInfoDisabled +# enabled:e.healthInfoDisabled +# /var/packages/StorageManager/target/ui/storage_panel.js -scriptver="v2.1.38" +scriptver="v3.6.124" script=Synology_HDD_db repo="007revad/Synology_HDD_db" +scriptname=syno_hdd_db + +# Check BASH variable is bash +if [ ! "$(basename "$BASH")" = bash ]; then + echo "This is a bash script. Do not run it with $(basename "$BASH")" + printf \\a + exit 1 +fi -#echo -e "bash version: $(bash --version | head -1 | cut -d' ' -f4)\n" # debug +# Check script is running on a Synology NAS +if ! /usr/bin/uname -a | grep -q -i synology; then + echo "This script is NOT running on a Synology NAS!" + echo "Copy the script to a folder on the Synology" + echo "and run it from there." + exit 1 +fi -# Shell Colors -#Black='\e[0;30m' -Red='\e[0;31m' -#Green='\e[0;32m' -Yellow='\e[0;33m' -#Blue='\e[0;34m' -#Purple='\e[0;35m' -Cyan='\e[0;36m' -#White='\e[0;37m' -Error='\e[41m' -Off='\e[0m' - - -usage(){ +ding(){ + printf \\a +} + +usage(){ cat <_host db and db.new file(s) - -n, --noupdate Prevent DSM updating the compatible drive databases - -m, --m2 Don't process M.2 drives - -f, --force Force DSM to not check drive compatibility - -r, --ram Disable memory compatibility checking - -h, --help Show this help message - -v, --version Show the script version - + -s, --showedits Show edits made to _host db and db.new file(s) + -n, --noupdate Prevent DSM updating the compatible drive databases + -r, --ram Disable memory compatibility checking (DSM 7.x only) + and sets max memory to the amount of installed memory + -f, --force Force DSM to not check drive compatibility + Do not use this option unless absolutely needed + -i, --incompatible Change incompatible drives to supported + Do not use this option unless absolutely needed + -w, --wdda Disable WD Device Analytics to prevent DSM showing + a false warning for WD drives that are 3 years old + DSM 7.2.1 and later already has WDDA disabled + -p, --pcie Enable creating volumes on M2 in unknown PCIe adaptor + -e, --email Disable colored text in output scheduler emails + -S, --ssd=DRIVE Enable write_mostly on internal HDDs so DSM primarily + reads from internal SSDs or your specified drives + -S automatically sets internal SSDs as DSM preferred + --ssd=DRIVE requires the fast drive(s) as argument, + or restore as the argument to reset drives to default + --ssd=sata1 or --ssd=sata1,sata2 or --ssd=sda etc + --ssd=restore + --restore Undo all changes made by the script (except -S --ssd) + To restore all changes including write_mostly use + --restore --ssd=restore + --autoupdate=AGE Auto update script (useful when script is scheduled) + AGE is how many days old a release must be before + auto-updating. AGE must be a number: 0 or greater + -I, --ihm Update IronWolf Health Management to 2.5.1 to support + recent model IronWolf and IronWolf Pro drives. + For NAS with x86_64 CPUs only + Installs IHM on '22 series and newer models (untested) + --reboot Reboot after a DSM update when build number has changed + Only works if script is running from a boot-up schedule + Only needed if NVMe volume or PCIe card need a 2nd + reboot after DSM update + -h, --help Show this help message + -v, --version Show the script version + EOF exit 0 } -scriptversion(){ +scriptversion(){ cat <0; i--)) + do printf -v f "%s%s()" "$f" "${FUNCNAME[i]}" + done + + printf "$y%s:%04d$c%s$n " "${BASH_SOURCE[1]##*/}" "$lineno" "$f" +} if [[ $debug == "yes" ]]; then - # set -x - export PS4='`[[ $? == 0 ]] || echo "\e[1;31;40m($?)\e[m\n "`:.$LINENO:' + PS4='\r$(PS4func $LINENO)' + set -o xtrace +fi + + +# Shell Colors +if [[ $color != "no" ]]; then + #Black='\e[0;30m' # ${Black} + Red='\e[0;31m' # ${Red} + #Green='\e[0;32m' # ${Green} + Yellow='\e[0;33m' # ${Yellow} + #Blue='\e[0;34m' # ${Blue} + #Purple='\e[0;35m' # ${Purple} + Cyan='\e[0;36m' # ${Cyan} + #White='\e[0;37m' # ${White} + Error='\e[41m' # ${Error} + Warn='\e[47;31m' # ${Warn} + Off='\e[0m' # ${Off} +else + echo "" # For task scheduler email readability fi # Check script is running as root if [[ $( whoami ) != "root" ]]; then - echo -e "${Error}ERROR${Off} This script must be run as root or sudo!" + ding + echo -e "${Error}ERROR${Off} This script must be run as sudo or root!" exit 1 fi +detect_scheduler(){ + # Check if stdin is a terminal (interactive) + [ ! -t 0 ] && return 0 + + # Check parent process + local parent + parent=$(ps -p $PPID -o comm=) + [[ "$parent" =~ (systemd-run|sched|crond) ]] && return 0 + + return 1 +} + # Get DSM major version -dsm=$(get_key_value /etc.defaults/VERSION majorversion) +dsm=$(/usr/syno/bin/synogetkeyvalue /etc.defaults/VERSION majorversion) if [[ $dsm -gt "6" ]]; then version="_v$dsm" fi +# Get DSM major and minor version +major=$(/usr/syno/bin/synogetkeyvalue /etc.defaults/VERSION majorversion) +minor=$(/usr/syno/bin/synogetkeyvalue /etc.defaults/VERSION minorversion) +dsmversion="$major$minor" + # Get Synology model +model=$(cat /proc/sys/kernel/syno_hw_version) +modelname="$model" -# This doesn't work for drives migrated from different model -#model=$(find /var/lib/disk-compatibility -regextype egrep -regex ".*host(_v7)?\.db$" |\ -# cut -d"/" -f5 | cut -d"_" -f1 | uniq) +# Get CPU platform_name +#platform_name=$(/usr/syno/bin/synogetkeyvalue /etc.defaults/synoinfo.conf platform_name) -model=$(cat /proc/sys/kernel/syno_hw_version) +# Get CPU arch +arch="$(uname -m)" # Show script version @@ -253,15 +297,15 @@ model=$(cat /proc/sys/kernel/syno_hw_version) echo "$script $scriptver" # Get DSM full version -productversion=$(get_key_value /etc.defaults/VERSION productversion) -buildphase=$(get_key_value /etc.defaults/VERSION buildphase) -buildnumber=$(get_key_value /etc.defaults/VERSION buildnumber) -smallfixnumber=$(get_key_value /etc.defaults/VERSION smallfixnumber) +productversion=$(/usr/syno/bin/synogetkeyvalue /etc.defaults/VERSION productversion) +buildphase=$(/usr/syno/bin/synogetkeyvalue /etc.defaults/VERSION buildphase) +buildnumber=$(/usr/syno/bin/synogetkeyvalue /etc.defaults/VERSION buildnumber) +smallfixnumber=$(/usr/syno/bin/synogetkeyvalue /etc.defaults/VERSION smallfixnumber) # Show DSM full version and model if [[ $buildphase == GM ]]; then buildphase=""; fi if [[ $smallfixnumber -gt "0" ]]; then smallfix="-$smallfixnumber"; fi -echo "$model DSM $productversion-$buildnumber$smallfix $buildphase" +echo "$model $arch DSM $productversion-$buildnumber$smallfix $buildphase" # Convert model to lower case @@ -269,34 +313,104 @@ model=${model,,} # Check for dodgy characters after model number if [[ $model =~ 'pv10-j'$ ]]; then # GitHub issue #10 - model=${model%??????}+ # replace last 6 chars with + + modelname=${modelname%??????}+ # replace last 6 chars with + + model=${model%??????}+ # replace last 6 chars with + echo -e "\nUsing model: $model" elif [[ $model =~ '-j'$ ]]; then # GitHub issue #2 - model=${model%??} # remove last 2 chars + modelname=${modelname%??} # remove last 2 chars + model=${model%??} # remove last 2 chars echo -e "\nUsing model: $model" fi +# Get StorageManager version +storagemgrver=$(/usr/syno/bin/synopkg version StorageManager) +# Show StorageManager version +if [[ $storagemgrver ]]; then echo -e "- StorageManager $storagemgrver"; fi + +# Get SynoOnlinePack version +if [[ $dsmversion -gt "72" ]]; then + # Is DSM 7.3 or later + if [[ -f /var/packages/SynoOnlinePack_v3/INFO ]]; then + SOPinfo="/var/packages/SynoOnlinePack_v3/INFO" + v2="_v3" + elif [[ -f /var/packages/SynoOnlinePack_v2/INFO ]]; then + SOPinfo="/var/packages/SynoOnlinePack_v2/INFO" + v2="_v2" + else + SOPinfo="/var/packages/SynoOnlinePack/INFO" + fi + SOPpkgver="$(/usr/syno/bin/synogetkeyvalue $SOPinfo version)" + #echo -e "SynoOnlinePack$v2 version $SOPpkgver\n" + echo "- SynoOnlinePack$v2 version $SOPpkgver" +#else +# echo "" +fi + +# Show host drive db version +if [[ -f "/var/lib/disk-compatibility/${model}_host_v7.version" ]]; then + echo -n "- ${model}_host_v7 version " + cat "/var/lib/disk-compatibility/${model}_host_v7.version" + echo -e "\n" +fi +if [[ -f "/var/lib/disk-compatibility/${model}_host.version" ]]; then + echo -n "- ${model}_host version " + cat "/var/lib/disk-compatibility/${model}_host.version" + echo -e "\n" +fi + + # Show options used -echo "Using options: $args" +if [[ ${#args[@]} -gt "0" ]]; then + echo "Using options: ${args[*]}" +fi #echo "" # To keep output readable +# shellcheck disable=SC2317 # Don't warn about unreachable commands in this function +pause(){ + # When debugging insert pause command where needed + read -s -r -n 1 -p "Press any key to continue..." + read -r -t 0.1 -s -e -- # Silently consume all input + stty echo echok # Ensure read didn't disable echoing user input + echo -e "\n" +} + + #------------------------------------------------------------------------------ # Check latest release with GitHub API -get_latest_release() { - # Curl timeout options: - # https://unix.stackexchange.com/questions/94604/does-curl-have-a-timeout - curl --silent -m 10 --connect-timeout 5 \ - "https://api.github.com/repos/$1/releases/latest" | - grep '"tag_name":' | # Get tag line - sed -E 's/.*"([^"]+)".*/\1/' # Pluck JSON value +syslog_set(){ + if [[ ${1,,} == "info" || ${1,,} == "warn" || ${1,,} == "err" ]]; then + if [[ $autoupdate == "yes" ]]; then + # Add entry to Synology system log + /usr/syno/bin/synologset1 sys "$1" 0x11100000 "$2" + fi + fi } -tag=$(get_latest_release "$repo") + +# Get latest release info +# Curl timeout options: +# https://unix.stackexchange.com/questions/94604/does-curl-have-a-timeout +release=$(curl --silent -m 10 --connect-timeout 5 \ + "https://api.github.com/repos/$repo/releases/latest") + +# Release version +tag=$(echo "$release" | grep '"tag_name":' | sed -E 's/.*"([^"]+)".*/\1/') shorttag="${tag:1}" -#scriptpath=$(dirname -- "$0") + +# Release published date +published=$(echo "$release" | grep '"published_at":' | sed -E 's/.*"([^"]+)".*/\1/') +published="${published:0:10}" +published=$(date -d "$published" '+%s') + +# Today's date +now=$(date '+%s') + +# Days since release published +age=$(((now - published)/(60*60*24))) + # Get script location # https://stackoverflow.com/questions/59895/ @@ -304,253 +418,829 @@ source=${BASH_SOURCE[0]} while [ -L "$source" ]; do # Resolve $source until the file is no longer a symlink scriptpath=$( cd -P "$( dirname "$source" )" >/dev/null 2>&1 && pwd ) source=$(readlink "$source") - # If $source was a relative symlink, we need to resolve it + # If $source was a relative symlink, we need to resolve it # relative to the path where the symlink file was located [[ $source != /* ]] && source=$scriptpath/$source done scriptpath=$( cd -P "$( dirname "$source" )" >/dev/null 2>&1 && pwd ) +scriptfile=$( basename -- "$source" ) +echo "Running from: ${scriptpath}/$scriptfile" + #echo "Script location: $scriptpath" # debug +#echo "Source: $source" # debug +#echo "Script filename: $scriptfile" # debug + +#echo "tag: $tag" # debug +#echo "scriptver: $scriptver" # debug + + +cleanup_tmp(){ + cleanup_err= + + # Delete downloaded .tar.gz file + if [[ -f "/tmp/$script-$shorttag.tar.gz" ]]; then + if ! rm "/tmp/$script-$shorttag.tar.gz"; then + echo -e "${Error}ERROR${Off} Failed to delete"\ + "downloaded /tmp/$script-$shorttag.tar.gz!" >&2 + cleanup_err=1 + fi + fi + + # Delete extracted tmp files + if [[ -d "/tmp/$script-$shorttag" ]]; then + if ! rm -r "/tmp/$script-$shorttag"; then + echo -e "${Error}ERROR${Off} Failed to delete"\ + "downloaded /tmp/$script-$shorttag!" >&2 + cleanup_err=1 + fi + fi + + # Add warning to DSM log + if [[ $cleanup_err ]]; then + syslog_set warn "$script update failed to delete tmp files" + fi +} if ! printf "%s\n%s\n" "$tag" "$scriptver" | - sort --check --version-sort &> /dev/null ; then + sort --check=quiet --version-sort >/dev/null ; then echo -e "\n${Cyan}There is a newer version of this script available.${Off}" echo -e "Current version: ${scriptver}\nLatest version: $tag" - if [[ -f $scriptpath/$script-$shorttag.tar.gz ]]; then + scriptdl="$scriptpath/$script-$shorttag" + if [[ -f ${scriptdl}.tar.gz || -f ${scriptdl}.zip ]]; then # They have the latest version tar.gz downloaded but are using older version - echo "https://github.com/$repo/releases/latest" + echo "You have the latest version downloaded but are using an older version" sleep 10 - elif [[ -d $scriptpath/$script-$shorttag ]]; then + elif [[ -d $scriptdl ]]; then # They have the latest version extracted but are using older version - echo "https://github.com/$repo/releases/latest" + echo "You have the latest version extracted but are using an older version" sleep 10 else - echo -e "${Cyan}Do you want to download $tag now?${Off} [y/n]" - read -r -t 30 reply + if [[ $autoupdate == "yes" ]]; then + if [[ $age -gt "$delay" || $age -eq "$delay" ]]; then + echo "Downloading $tag" + reply=y + else + echo "Skipping as $tag is less than $delay days old." + fi + else + echo -e "${Cyan}Do you want to download $tag now?${Off} [y/n]" + read -r -t 30 reply + fi + if [[ ${reply,,} == "y" ]]; then + # Delete previously downloaded .tar.gz file and extracted tmp files + cleanup_tmp + if cd /tmp; then url="https://github.com/$repo/archive/refs/tags/$tag.tar.gz" - if ! curl -LJO -m 30 --connect-timeout 5 "$url"; - then - echo -e "${Error}ERROR ${Off} Failed to download"\ + if ! curl -JLO -m 30 --connect-timeout 5 "$url"; then + echo -e "${Error}ERROR${Off} Failed to download"\ "$script-$shorttag.tar.gz!" + syslog_set warn "$script $tag failed to download" else if [[ -f /tmp/$script-$shorttag.tar.gz ]]; then # Extract tar file to /tmp/ if ! tar -xf "/tmp/$script-$shorttag.tar.gz" -C "/tmp"; then - echo -e "${Error}ERROR ${Off} Failed to"\ + echo -e "${Error}ERROR${Off} Failed to"\ "extract $script-$shorttag.tar.gz!" + syslog_set warn "$script failed to extract $script-$shorttag.tar.gz!" else - # Copy new script sh files to script location - if ! cp -p "/tmp/$script-$shorttag/"*.sh "$scriptpath"; then + # Set script sh files as executable + if ! chmod a+x "/tmp/$script-$shorttag/"*.sh ; then + permerr=1 + echo -e "${Error}ERROR${Off} Failed to set executable permissions" + syslog_set warn "$script failed to set permissions on $tag" + fi + + # Copy new script sh file to script location + if ! cp -p "/tmp/$script-$shorttag/${scriptname}.sh" "${scriptpath}/${scriptfile}"; + then copyerr=1 - echo -e "${Error}ERROR ${Off} Failed to copy"\ - "$script-$shorttag .sh file(s) to:\n $scriptpath" - else - # Set permsissions on CHANGES.txt - if ! chmod 744 "$scriptpath/"*.sh ; then - permerr=1 - echo -e "${Error}ERROR ${Off} Failed to set permissions on:" - echo "$scriptpath *.sh file(s)" - fi + echo -e "${Error}ERROR${Off} Failed to copy"\ + "$script-$shorttag sh file(s) to:\n $scriptpath/${scriptfile}" + syslog_set warn "$script failed to copy $tag to script location" fi - # Copy new CHANGES.txt file to script location - if ! cp -p "/tmp/$script-$shorttag/CHANGES.txt" "$scriptpath"; then + # Copy new script companion sh file to script location + if ! cp -p "/tmp/$script-$shorttag/syno_hdd_shutdown.sh" "${scriptpath}/syno_hdd_shutdown.sh"; + then copyerr=1 - echo -e "${Error}ERROR ${Off} Failed to copy"\ - "$script-$shorttag/CHANGES.txt to:\n $scriptpath" - else - # Set permsissions on CHANGES.txt - if ! chmod 744 "$scriptpath/CHANGES.txt"; then + echo -e "${Error}ERROR${Off} Failed to copy"\ + "$script-$shorttag sh file(s) to:\n $scriptpath/syno_hdd_shutdown.sh" + syslog_set warn "$script failed to copy syno_hdd_shutdown.sh to script location" + fi + + # Copy new syno_hdd_vendor_ids.txt file + vidstxt="syno_hdd_vendor_ids.txt" + if [[ $scriptpath =~ /volume* ]]; then + if [[ ! -f "$scriptpath/$vidstxt" ]]; then # Don't overwrite file + # Copy new syno_hdd_vendor_ids.txt file to script location + if ! cp -p "/tmp/$script-$shorttag/$vidstxt" "$scriptpath"; then + if [[ $autoupdate != "yes" ]]; then copyerr=1; fi + echo -e "${Error}ERROR${Off} Failed to copy"\ + "$script-$shorttag/$vidstxt to:\n $scriptpath" + else + # Set permissions on syno_hdd_vendor_ids.txt + if ! chmod 755 "$scriptpath/$vidstxt"; then + if [[ $autoupdate != "yes" ]]; then permerr=1; fi + echo -e "${Error}ERROR${Off} Failed to set permissions on:" + echo "$scriptpath/$vidstxt" + fi + vids_txt=", syno_hdd_vendor_ids.txt" + fi + fi + fi + + # Copy new CHANGES.txt file to script location (if script on a volume) + if [[ $scriptpath =~ /volume* ]]; then + # Set permissions on CHANGES.txt + if ! chmod 664 "/tmp/$script-$shorttag/CHANGES.txt"; then permerr=1 - echo -e "${Error}ERROR ${Off} Failed to set permissions on:" + echo -e "${Error}ERROR${Off} Failed to set permissions on:" echo "$scriptpath/CHANGES.txt" fi - fi - # Delete downloaded .tar.gz file - if ! rm "/tmp/$script-$shorttag.tar.gz"; then - delerr=1 - echo -e "${Error}ERROR ${Off} Failed to delete"\ - "downloaded /tmp/$script-$shorttag.tar.gz!" + # Copy new CHANGES.txt file to script location + if ! cp -p "/tmp/$script-$shorttag/CHANGES.txt"\ + "${scriptpath}/${scriptname}_CHANGES.txt"; + then + if [[ $autoupdate != "yes" ]]; then copyerr=1; fi + echo -e "${Error}ERROR${Off} Failed to copy"\ + "$script-$shorttag/CHANGES.txt to:\n $scriptpath" + else + changestxt=" and changes.txt" + fi fi - # Delete extracted tmp files - if ! rm -r "/tmp/$script-$shorttag"; then - delerr=1 - echo -e "${Error}ERROR ${Off} Failed to delete"\ - "downloaded /tmp/$script-$shorttag!" - fi + # Delete downloaded tmp files + cleanup_tmp # Notify of success (if there were no errors) - if [[ $copyerr != 1 ]] && [[ $permerr != 1 ]]; then - echo -e "\n$tag and changes.txt downloaded to:"\ - "$scriptpath" - echo -e "${Cyan}Do you want to stop this script"\ - "so you can run the new one?${Off} [y/n]" - read -r reply - if [[ ${reply,,} == "y" ]]; then exit; fi + if [[ $copyerr != 1 && $permerr != 1 ]]; then + echo -e "\n$tag ${scriptfile}$vids_txt$changestxt downloaded to: ${scriptpath}\n" + syslog_set info "$script successfully updated to $tag" + + # Reload script + printf -- '-%.0s' {1..79}; echo # print 79 - + exec "${scriptpath}/$scriptfile" "${args[@]}" + else + syslog_set warn "$script update to $tag had errors" fi fi else - echo -e "${Error}ERROR ${Off}"\ + echo -e "${Error}ERROR${Off}"\ "/tmp/$script-$shorttag.tar.gz not found!" #ls /tmp | grep "$script" # debug + syslog_set warn "/tmp/$script-$shorttag.tar.gz not found" fi fi + cd "$scriptpath" || echo -e "${Error}ERROR${Off} Failed to cd to script location!" else - echo -e "${Error}ERROR ${Off} Failed to cd to /tmp!" + echo -e "${Error}ERROR${Off} Failed to cd to /tmp!" + syslog_set warn "$script update failed to cd to /tmp" fi fi fi fi -#------------------------------------------------------------------------------ -# Get list of installed SATA, SAS and M.2 NVMe/SATA drives, -# PCIe M.2 cards and connected Expansion Units. - -fixdrivemodel(){ - # Remove " 00Y" from end of Samsung/Lenovo SSDs - # To fix issue #13 - if [[ $1 =~ MZ.*" 00Y" ]]; then - hdmodel=$(printf "%s" "$1" | sed 's/ 00Y.*//') - fi +# Show if running in shell or via task scheduler +if detect_scheduler; then + echo "Running via task scheduler" + sch_task="yes" +else + echo "Running in interactive shell" + sch_task="" +fi - # Brands that return "BRAND " and need "BRAND " removed. - if [[ $1 =~ ^[A-Za-z]{1,7}" ".* ]]; then - #see Smartmontools database in /var/lib/smartmontools/drivedb.db - hdmodel=${hdmodel#"WDC "} # Remove "WDC " from start of model name - hdmodel=${hdmodel#"HGST "} # Remove "HGST " from start of model name - hdmodel=${hdmodel#"TOSHIBA "} # Remove "TOSHIBA " from start of model name - # Old drive brands - hdmodel=${hdmodel#"Hitachi "} # Remove "Hitachi " from start of model name - hdmodel=${hdmodel#"SAMSUNG "} # Remove "SAMSUNG " from start of model name - hdmodel=${hdmodel#"FUJISTU "} # Remove "FUJISTU " from start of model name - hdmodel=${hdmodel#"APPLE HDD "} # Remove "APPLE HDD " from start of model name +# Warn if script located on M.2 drive +get_script_vol() { + local script_root vol_num vg_name + script_root="${scriptpath#/*}" + script_root="${script_root%%/*}" + if [[ $script_root =~ ^volume ]]; then + vol_num="${script_root:6}" + vg_name=$(lvs --noheadings --select=lv_name="volume_$vol_num" --options=vg_name) + vg_name="${vg_name// }" + # Only get first partition on volume group + vol_name=$(pvs --noheadings --select=vg_name="$vg_name" --options=pv_name | awk '{print $1}' | head -n 1) + else + vol_name=$(df --output=source "/$script_root" | sed 1d) # sed 1d = delete first line fi } +get_script_vol # sets $vol_name to /dev/whatever +if grep -qE "^${vol_name#/dev/} .+ nvme" /proc/mdstat; then + ding + echo -e "\n${Yellow}WARNING${Off} Don't store this script on an NVMe volume!" + exit 3 +fi -getdriveinfo() { - # $1 is /sys/block/sata1 etc - # Skip USB drives - usb=$(grep $(basename -- "$1") /proc/mounts | grep usb | cut -d" " -f1-2) - if [[ ! $usb ]]; then +#------------------------------------------------------------------------------ +# Set file variables - # Get drive model and firmware version - hdmodel=$(cat "$1/device/model") - hdmodel=$(printf "%s" "$hdmodel" | xargs) # trim leading and trailing white space +if [[ -f /etc.defaults/model.dtb ]]; then # Is device tree model + # Get syn_hw_revision, r1 or r2 etc (or just a linefeed if not a revision) + hwrevision=$(cat /proc/sys/kernel/syno_hw_revision) - # Fix dodgy model numbers - fixdrivemodel "$hdmodel" + # If syno_hw_revision is r1 or r2 it's a real Synology, + # and I need to edit model_rN.dtb instead of model.dtb + if [[ $hwrevision =~ r[0-9] ]]; then + #echo "hwrevision: $hwrevision" # debug + hwrev="_$hwrevision" + fi + + dtb_file="/etc.defaults/model${hwrev}.dtb" + dtb2_file="/etc/model${hwrev}.dtb" + #dts_file="/etc.defaults/model${hwrev}.dts" + dts_file="/tmp/model${hwrev}.dts" +fi + +adapter_cards="/usr/syno/etc.defaults/adapter_cards.conf" +adapter_cards2="/usr/syno/etc/adapter_cards.conf" +dbpath=/var/lib/disk-compatibility/ +synoinfo="/etc.defaults/synoinfo.conf" - fwrev=$(cat "$1/device/rev") - fwrev=$(printf "%s" "$fwrev" | xargs) # trim leading and trailing white space +if [[ $buildnumber -gt 64570 ]]; then + # DSM 7.2.1 and later + #strgmgr="/var/packages/StorageManager/target/ui/storage_panel.js" + strgmgr="/usr/local/packages/@appstore/StorageManager/ui/storage_panel.js" +elif [[ $buildnumber -ge 42962 ]]; then + # DSM 7.1.1 to 7.2 + strgmgr="/usr/syno/synoman/webman/modules/StorageManager/storage_panel.js" +fi +vidfile="/usr/syno/etc.defaults/pci_vendor_ids.conf" +vidfile2="/usr/syno/etc/pci_vendor_ids.conf" + +reboot_file="${scriptpath}/syno_hdd_reboot.txt" +if [[ ! -f "$reboot_file" ]]; then + echo "Do NOT delete this file!" > "$reboot_file" + echo "It is used to track if DSM has updated." >> "$reboot_file" + synosetkeyvalue "$reboot_file" dsm_build "$buildnumber" +fi - if [[ $hdmodel ]] && [[ $fwrev ]]; then - hdlist+=("${hdmodel},${fwrev}") + +set_writemostly(){ + # $1 is writemostly or -writemostly + # $2 is sata1 or sas1 or sda etc + local model + # Show drive model + model="$(xargs < /sys/block/"${2}"/device/model)" + echo -e "${Yellow}$model${Off}" + + if [[ ${1::2} == "sd" ]]; then + # sda etc + # md0 DSM system partition + if [[ -d /sys/block/md0/md/dev-"${2}"1 ]]; then + echo "$1" > /sys/block/md0/md/dev-"${2}"1/state + # Show setting + echo -n " $2 DSM partition: " + cat /sys/block/md0/md/dev-"${2}"1/state fi - fi -} -getm2info() { - # $1 is /sys/block/nvme0n1 etc - nvmemodel=$(cat "$1/device/model") - nvmemodel=$(printf "%s" "$nvmemodel" | xargs) # trim leading and trailing white space - if [[ $2 == "nvme" ]]; then - nvmefw=$(cat "$1/device/firmware_rev") - elif [[ $2 == "nvc" ]]; then - nvmefw=$(cat "$1/device/rev") - fi - nvmefw=$(printf "%s" "$nvmefw" | xargs) # trim leading and trailing white space + # md1 DSM swap partition + if [[ -d /sys/block/md1/md/dev-"${2}"2 ]]; then + echo "$1" > /sys/block/md1/md/dev-"${2}"2/state + # Show setting + echo -n " $2 Swap partition: " + cat /sys/block/md1/md/dev-"${2}"2/state + fi + else + # sata1 or sas1 etc + # md0 DSM system partition + if [[ -d /sys/block/md0/md/dev-"${2}"p1 ]]; then + echo "$1" > /sys/block/md0/md/dev-"${2}"p1/state + # Show setting + echo -n " $2 DSM partition: " + cat /sys/block/md0/md/dev-"${2}"p1/state + fi - if [[ $nvmemodel ]] && [[ $nvmefw ]]; then - nvmelist+=("${nvmemodel},${nvmefw}") + # md1 DSM swap partition + if [[ -d /sys/block/md1/md/dev-"${2}"p2 ]]; then + echo "$1" > /sys/block/md1/md/dev-"${2}"p2/state + # Show setting + echo -n " $2 Swap partition: " + cat /sys/block/md1/md/dev-"${2}"p2/state + fi fi } -getcardmodel() { - # Get M.2 card model (if M.2 drives found) - # $1 is /dev/nvme0n1 etc - if [[ ${#nvmelist[@]} -gt "0" ]]; then - cardmodel=$(synodisk --m2-card-model-get "$1") - if [[ $cardmodel =~ M2D[0-9][0-9] ]]; then - # M2 adaptor card - m2carddblist+=("${model}_${cardmodel,,}${version}.db") # M.2 card's db file - m2cardlist+=("$cardmodel") # M.2 card - elif [[ $cardmodel =~ E[0-9][0-9]+M.+ ]]; then - # Ethernet + M2 adaptor card - m2carddblist+=("${model}_${cardmodel,,}${version}.db") # M.2 card's db file - m2cardlist+=("$cardmodel") # M.2 card + +#------------------------------------------------------------------------------ +# Restore changes from backups + +if [[ $restore == "yes" ]]; then + dbbaklist=($(find $dbpath -maxdepth 1 \( -name "*.db.new.bak" -o -name "*.db.bak" \))) + # Sort array + IFS=$'\n' + dbbakfiles=($(sort <<<"${dbbaklist[*]}")) + unset IFS + + echo "" + if [[ ${#dbbakfiles[@]} -gt "0" || -f ${synoinfo}.bak ||\ + -f ${dtb_file}.bak || -f ${adapter_cards}.bak ]] ; then + + # Restore synoinfo.conf from backup + if [[ -f ${synoinfo}.bak ]]; then + keyvalues=("support_disk_compatibility" "support_memory_compatibility") + keyvalues+=("mem_max_mb" "supportnvme" "support_m2_pool" "support_wdda") + for v in "${!keyvalues[@]}"; do + defaultval="$(/usr/syno/bin/synogetkeyvalue ${synoinfo}.bak "${keyvalues[v]}")" + currentval="$(/usr/syno/bin/synogetkeyvalue ${synoinfo} "${keyvalues[v]}")" + if [[ $currentval != "$defaultval" ]]; then + if /usr/syno/bin/synosetkeyvalue "$synoinfo" "${keyvalues[v]}" "$defaultval"; + then + echo "Restored ${keyvalues[v]} = $defaultval" + fi + fi + done fi - fi -} + # Delete "drive_db_test_url=127.0.0.1" line (and line break) from synoinfo.conf + sed -i "/drive_db_test_url=*/d" "$synoinfo" + sed -i "/drive_db_test_url=*/d" /etc/synoinfo.conf -for d in /sys/block/*; do - # $d is /sys/block/sata1 etc - case "$(basename -- "${d}")" in - sd*|hd*) - if [[ $d =~ [hs]d[a-z][a-z]?$ ]]; then - # Get drive model and firmware version - getdriveinfo "$d" + # Restore adapter_cards.conf from backup + # /usr/syno/etc.defaults/adapter_cards.conf + if [[ -f ${adapter_cards}.bak ]]; then + if cp -p "${adapter_cards}.bak" "${adapter_cards}"; then + echo "Restored ${adapter_cards}" + else + restoreerr=1 + echo -e "${Error}ERROR${Off} Failed to restore ${adapter_cards}!\n" fi - ;; - sata*|sas*) - if [[ $d =~ (sas|sata)[0-9][0-9]?[0-9]?$ ]]; then - # Get drive model and firmware version - getdriveinfo "$d" + # /usr/syno/etc/adapter_cards.conf + if cp -p "${adapter_cards}.bak" "${adapter_cards2}"; then + echo -e "Restored ${adapter_cards2}" + else + restoreerr=1 + echo -e "${Error}ERROR${Off} Failed to restore ${adapter_cards2}!\n" fi - ;; - nvme*) - if [[ $d =~ nvme[0-9][0-9]?n[0-9][0-9]?$ ]]; then - if [[ $m2 != "no" ]]; then - getm2info "$d" "nvme" - # Get M.2 card model if in M.2 card - getcardmodel "/dev/$(basename -- "${d}")" - # Enable creating M.2 storage pool and volume in Storage Manager - echo 1 > /run/synostorage/disks/$(basename -- "$d")/m2_pool_support - fi + # Make sure they don't lose E10M20-T1 network connection + modelrplowercase=${modelname//RP/rp} + /usr/syno/bin/set_section_key_value ${adapter_cards} E10M20-T1_sup_nic "$modelrplowercase" + /usr/syno/bin/set_section_key_value ${adapter_cards2} E10M20-T1_sup_nic "$modelrplowercase" + fi + + # Restore model.dtb from backup + if [[ -f ${dtb_file}.bak ]]; then + # /etc.default/model.dtb + if cp -p "${dtb_file}.bak" "${dtb_file}"; then + echo "Restored ${dtb_file}" + else + restoreerr=1 + echo -e "${Error}ERROR${Off} Failed to restore ${dtb_file}!\n" fi - ;; - nvc*) # M.2 SATA drives (in PCIe card only?) - #if [[ $d =~ nvc[0-9][0-9]?p[0-9][0-9]?$ ]]; then - if [[ $d =~ nvc[0-9][0-9]?$ ]]; then - if [[ $m2 != "no" ]]; then - getm2info "$d" "nvc" - # Get M.2 card model if in M.2 card - getcardmodel "/dev/$(basename -- "${d}")" + # Restore /etc/model.dtb from /etc.default/model.dtb + if cp -p "${dtb_file}.bak" "${dtb2_file}"; then + echo -e "Restored ${dtb2_file}" + else + restoreerr=1 + echo -e "${Error}ERROR${Off} Failed to restore ${dtb2_file}!\n" + fi + fi - # Enable creating M.2 storage pool and volume in Storage Manager - echo 1 > /run/synostorage/disks/$(basename -- "$d")/m2_pool_support - fi + # Restore storage_panel.js from backup + if [[ $buildnumber -gt 64570 ]]; then + # DSM 7.2.1 and later + strgmgrver="$(/usr/syno/bin/synopkg version StorageManager)" + elif [[ $buildnumber -ge 42962 ]]; then + # DSM 7.1.1 to 7.2 + strgmgrver="${buildnumber}${smallfixnumber}" + fi + if [[ -f "${strgmgr}.$strgmgrver" ]]; then + if cp -p "${strgmgr}.$strgmgrver" "$strgmgr"; then + echo "Restored $(basename -- "$strgmgr")" + else + restoreerr=1 + echo -e "${Error}ERROR${Off} Failed to restore $(basename -- "$strgmgr")!\n" fi - ;; - esac -done + else + echo "No backup of $(basename -- "$strgmgr") found." + fi + echo "" + # Restore .db files from backups + for f in "${!dbbakfiles[@]}"; do + replaceme="${dbbakfiles[f]%.bak}" # Remove .bak + if cp -p "${dbbakfiles[f]}" "$replaceme"; then + echo "Restored $(basename -- "$replaceme")" + else + restoreerr=1 + echo -e "${Error}ERROR${Off} Failed to restore $(basename -- "$replaceme")!\n" + fi + done -# Sort hdlist array into new hdds array to remove duplicates -if [[ ${#hdlist[@]} -gt "0" ]]; then - while IFS= read -r -d '' x; do - hdds+=("$x") - done < <(printf "%s\0" "${hdlist[@]}" | sort -uz) -fi + # Delete any .dbr and .db.newr files left by previous script versions + for f in "${dbpath}"*dbr; do + if [[ -f $f ]]; then + rm "$f" >/dev/null + fi + done + for f in "${dbpath}"*db.newr; do + if [[ -f $f ]]; then + rm "$f" >/dev/null + fi + done -# Check hdds array isn't empty -if [[ ${#hdds[@]} -eq "0" ]]; then - echo -e "\n${Error}ERROR${Off} No drives found!" && exit 2 -else - echo -e "\nHDD/SSD models found: ${#hdds[@]}" - num="0" - while [[ $num -lt "${#hdds[@]}" ]]; do - echo "${hdds[num]}" - num=$((num +1)) - done + # Update .db files from Synology + /usr/syno/bin/syno_disk_db_update --update + + # Enable SynoMemCheck.service if disabled + memcheck="/usr/lib/systemd/system/SynoMemCheck.service" + if [[ $(/usr/syno/bin/synogetkeyvalue "$memcheck" ExecStart) == "/bin/true" ]]; then + /usr/syno/bin/synosetkeyvalue "$memcheck" ExecStart /usr/syno/bin/syno_mem_check + fi + + if [[ -z $restoreerr ]]; then + echo -e "\nRestore successful." + fi + + # Restore writemostly if set + if [[ $ssd_restore == "yes" ]]; then + # Get array of internal drives + readarray -t internal_drives < <(synodisk --enum -t internal | grep 'Disk path' | cut -d"/" -f3) + + # Restore all internal drives to just in_sync + echo -e "\nRestoring internal drive's state" + for idrive in "${internal_drives[@]}"; do + md0="/sys/block/md0/md/dev-" + md1="/sys/block/md1/md/dev-" + if [[ ${idrive::2} == "sd" ]]; then + # sda etc + # Check DSM system and swap partitions + if grep -q "write_mostly" "${md0}$idrive"1/state ||\ + grep -q "write_mostly" "${md1}$idrive"2/state; then + set_writemostly -writemostly "$idrive" + fi + else + # sata1 or sas1 etc + # Check DSM system and swap partitions + if grep -q "write_mostly" "${md0}$idrive"p1/state ||\ + grep -q "write_mostly" "${md1}$idrive"p2/state; then + set_writemostly -writemostly "$idrive" + fi + fi + done + fi + + else + echo "Nothing to restore." + fi + exit +fi + + +#------------------------------------------------------------------------------ +# Get list of installed SATA, SAS and M.2 NVMe/SATA drives, +# PCIe M.2 cards and connected Expansion Units. + +vendor_from_id(){ + # Vendor ids missing in /usr/syno/etc.defaults/pci_vendor_ids.conf + # $1 is vendor id + # https://devicehunt.com/all-pci-vendors + # https://pci-ids.ucw.cz/ + vendor="" + case "${1,,}" in + 0x10ec) vendor=TEAMGROUP ;; + 0x025e) vendor=Solidigm ;; + 0x1458) vendor=Gigabyte ;; + 0x1462) vendor=MSI ;; + 0x196e) vendor=PNY ;; + 0x1987) vendor=Phison ;; + 0x1b1c) vendor=Corsair ;; + 0x1c5c) vendor="SK Hynix" ;; + 0x1cc4) vendor=UMIS ;; + 0x1cfa) vendor=Corsair ;; # Memory only? + 0x1d97) vendor=SPCC/Lexar ;; # 2 brands with same vid + 0x1dbe) vendor=ADATA ;; + 0x1e0f) vendor=KIOXIA ;; + 0x1e49) vendor=ZHITAI ;; + 0x1e4b) vendor=HS/MAXIO ;; # 2 brands with same vid + 0x1f40) vendor=Netac ;; + + 0x1bdc) vendor=Apacer;; + 0x0ed1) vendor=aigo ;; + 0x05dc) vendor=Lexar ;; + 0x1d79) vendor=Transcend;; + *) + # Get vendor from syno_hdd_vendor_ids.txt + vidlist="$scriptpath/syno_hdd_vendor_ids.txt" + if [[ -r "$vidlist" ]]; then + val=$(/usr/syno/bin/synogetkeyvalue "$vidlist" "$1") + if [[ -n "$val" ]]; then + vendor="$val" + else + echo -e "\n${Yellow}WARNING${Off} No vendor found for vid $1" >&2 + echo -e "You can add ${Cyan}$1${Off} and your drive's vendor to: " >&2 + echo "$vidlist" >&2 + fi + else + echo -e "\n${Error}ERROR{OFF} $vidlist not found!" >&2 + fi + ;; + esac +} + +set_vendor(){ + # Add missing vendors to /usr/syno/etc.defaults/pci_vendor_ids.conf + if [[ $vendor ]]; then + # DS1817+, DS1517+, RS1219+, RS818+ don't have pci_vendor_ids.conf + if [[ "$vidfile" ]]; then + if ! grep -q "$vid" "$vidfile"; then + /usr/syno/bin/synosetkeyvalue "$vidfile" "${vid,,}" "$vendor" + val=$(/usr/syno/bin/synogetkeyvalue "$vidfile" "${vid,,}") + if [[ $val == "${vendor}" ]]; then + echo -e "\nAdded $vendor to pci_vendor_ids" >&2 + else + echo -e "\nFailed to add $vendor to pci_vendor_ids!" >&2 + fi + fi + if ! grep -q "$vid" "$vidfile2"; then + /usr/syno/bin/synosetkeyvalue "$vidfile2" "${vid,,}" "$vendor" + fi + + # Add leading 0 to short vid (change 0x5dc to 0x05dc) + if [[ ${#vid} -eq "5" ]]; then + vid="0x0${vid: -3}" + fi + if ! grep -q "$vid" "$vidfile"; then + /usr/syno/bin/synosetkeyvalue "$vidfile" "${vid,,}" "$vendor" + fi + if ! grep -q "$vid" "$vidfile2"; then + /usr/syno/bin/synosetkeyvalue "$vidfile2" "${vid,,}" "$vendor" + fi + + fi + fi +} + +get_vid(){ + # $1 is /dev/nvme0n1 etc + if [[ $1 ]]; then + vid=$(nvme id-ctrl "$1" | grep -E ^vid | awk '{print $NF}') + if [[ $vid ]]; then + val=$(/usr/syno/bin/synogetkeyvalue "$vidfile" "${vid,,}") + if [[ -z $val ]]; then + vendor_from_id "$vid" && set_vendor + fi + fi + fi +} + +fixdrivemodel(){ + # Remove " 00Y" from end of Samsung/Lenovo SSDs # Github issue #13 + if [[ $1 =~ MZ.*' 00Y' ]]; then + hdmodel=$(printf "%s" "$1" | sed 's/ 00Y.*//') + fi + + # Brands that return "BRAND " and need "BRAND " removed. + if [[ $1 =~ ^[A-Za-z]{3,7}' '.* ]]; then + # See Smartmontools database in /var/lib/smartmontools/drivedb.db + hdmodel=${hdmodel#"WDC "} # Remove "WDC " from start of model name + hdmodel=${hdmodel#"HGST "} # Remove "HGST " from start of model name + hdmodel=${hdmodel#"TOSHIBA "} # Remove "TOSHIBA " from start of model name + + # Chinese brand? + hdmodel=${hdmodel#"HCST "} # Remove "HCST " from start of model name. Issue #389 + + # Old drive brands + hdmodel=${hdmodel#"Hitachi "} # Remove "Hitachi " from start of model name + hdmodel=${hdmodel#"SAMSUNG "} # Remove "SAMSUNG " from start of model name + hdmodel=${hdmodel#"FUJISTU "} # Remove "FUJISTU " from start of model name + + # Remove any leading spaces + var=$(echo "$var" | sed -e 's/^[[:space:]]*//') + elif [[ $1 =~ ^'APPLE HDD '.* ]]; then + # Old drive brands + hdmodel=${hdmodel#"APPLE HDD "} # Remove "APPLE HDD " from start of model name + + # Remove any leading spaces + var=$(echo "$var" | sed -e 's/^[[:space:]]*//') + fi +} + +get_size_gb(){ + # $1 is /sys/block/sata1 or /sys/block/nvme0n1 etc + local disk_size_gb + disk_size_gb=$(synodisk --info /dev/"$(basename -- "$1")" 2>/dev/null | grep 'Total capacity' | awk '{print int($4 * 1.073741824)}') + echo "$disk_size_gb" +} + +getdriveinfo(){ + # $1 is /sys/block/sata1 etc + + # Skip USB drives + usb=$(grep "$(basename -- "$1")" /proc/mounts | grep "[Uu][Ss][Bb]" | cut -d" " -f1-2) + if [[ ! $usb ]]; then + + # Get drive model + hdmodel=$(cat "$1/device/model") + #hdmodel=$(printf "%s" "$hdmodel" | xargs) # trim leading and trailing white space (also replaces multiple spaces in model with 1 space) + hdmodel="$(printf "%s" "$hdmodel" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')" # trim leading and trailing white space + + # Fix dodgy model numbers + fixdrivemodel "$hdmodel" + + # Get drive firmware version + #fwrev=$(cat "$1/device/rev") + #fwrev=$(printf "%s" "$fwrev" | xargs) # trim leading and trailing white space + + device=/dev/"$(basename -- "$1")" + #fwrev=$(/usr/syno/bin/syno_hdd_util --ssd_detect | grep "$device " | awk '{print $2}') # GitHub issue #86, 87 + # Account for SSD drives with spaces in their model name/number + fwrev=$(/usr/syno/bin/syno_hdd_util --ssd_detect | grep "$device " | awk '{print $(NF-3)}') # GitHub issue #86, 87 + + # Get firmware version with smartctl if $fwrev null + # for M.2 SATA SSD and SAS drives. Github issue #407 + if [[ -z $fwrev ]]; then + dev=/dev/"$(basename -- "$1")" + fwrev=$(smartctl -a -d ata -T permissive "$dev" | grep -i firmware | awk '{print $NF}') + fi + + # Get drive GB size + size_gb=$(get_size_gb "$1") + if [[ -n "$size_gb" ]]; then # PR #187 + if [[ $hdmodel && $fwrev ]]; then + if /usr/syno/bin/synodisk --enum -t cache | grep -q /dev/"$(basename -- "$1")"; then + # Is SATA M.2 SSD + nvmelist+=("${hdmodel},${fwrev},${size_gb}") + else + hdlist+=("${hdmodel},${fwrev},${size_gb}") + fi + drivelist+=("${hdmodel}") + fi + fi + fi +} + +getm2info(){ + # $1 is /sys/block/nvme0n1 etc + nvmemodel=$(cat "$1/device/model") + #nvmemodel=$(printf "%s" "$nvmemodel" | xargs) # trim leading and trailing white space (also replaces multiple spaces in model with 1 space) + nvmemodel="$(printf "%s" "$nvmemodel" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')" # trim leading and trailing white space + if [[ $2 == "nvme" ]]; then + nvmefw=$(cat "$1/device/firmware_rev") + elif [[ $2 == "nvc" ]]; then + nvmefw=$(cat "$1/device/rev") + fi + #nvmefw=$(printf "%s" "$nvmefw" | xargs) # trim leading and trailing white space (also replaces multiple spaces in model with 1 space) + nvmefw="$(printf "%s" "$nvmefw" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')" # trim leading and trailing white space + + # Get drive GB size + size_gb=$(get_size_gb "$1") + + if [[ $nvmemodel && $nvmefw ]]; then + nvmelist+=("${nvmemodel},${nvmefw},${size_gb}") + drivelist+=("${nvmemodel}") + fi +} + +getcardmodel(){ + # Get M.2 card model (if M.2 drives found) + # $1 is /dev/nvme0n1 etc + if [[ ${#nvmelist[@]} -gt "0" ]]; then + cardmodel=$(/usr/syno/bin/synodisk --m2-card-model-get "$1") + if [[ $cardmodel =~ M2D[0-9][0-9] ]]; then + # M2 adaptor card + if [[ -f "${model}_${cardmodel,,}${version}.db" ]]; then + m2carddblist+=("${model}_${cardmodel,,}${version}.db") # M.2 card's db file + fi + if [[ -f "${model}_${cardmodel,,}.db" ]]; then + m2carddblist+=("${model}_${cardmodel,,}.db") # M.2 card's db file + fi + m2cardlist+=("$cardmodel") # M.2 card + elif [[ $cardmodel =~ E[0-9][0-9]+M.+ ]]; then + # Ethernet + M2 adaptor card + if [[ -f "${model}_${cardmodel,,}${version}.db" ]]; then + m2carddblist+=("${model}_${cardmodel,,}${version}.db") # M.2 card's db file + fi + if [[ -f "${model}_${cardmodel,,}.db" ]]; then + m2carddblist+=("${model}_${cardmodel,,}.db") # M.2 card's db file + fi + m2cardlist+=("$cardmodel") # M.2 card + fi + fi +} + +m2_pool_support(){ + # M.2 drives in M2 adaptor card do not officially support storage pools + if [[ -f /run/synostorage/disks/"$(basename -- "$1")"/m2_pool_support ]]; then # GitHub issue #86, 87 + echo -n 1 > /run/synostorage/disks/"$(basename -- "$1")"/m2_pool_support + fi +} + +m2_drive(){ + # $1 is nvme1 etc + # $2 is drive type (nvme or nvc) + if [[ $m2 != "no" ]]; then + # Check if is NVMe or SATA M.2 SSD + if /usr/syno/bin/synodisk --enum -t cache | grep -q /dev/"$(basename -- "$1")"; then + + if [[ $2 == "nvme" || $2 == "nvc" ]]; then + # Fix unknown vendor id if needed. GitHub issue #161 + # "Failed to get disk vendor" from synonvme --vendor-get + # causes "Unsupported firmware version" warning. + + # Skip for models without nvme command + if which synonvme >/dev/null; then + get_vid /dev/"$(basename -- "$1")" + fi + + # Get M2 model and firmware version + getm2info "$1" "$2" + fi + + # Get M.2 card model if in M.2 card + getcardmodel /dev/"$(basename -- "$1")" + + # Enable creating M.2 storage pool and volume in Storage Manager + m2_pool_support "$1" + + rebootmsg=yes # Show reboot message at end + fi + fi +} + +is_ssd(){ + # $1 is sda, sata1 or nvme0 + # Show TRIM warning if SSD or NVMe in RAID 5 or 6 + if ! synodisk --isssd /dev/"$1" >/dev/null; then + # exit code 0 = is not SSD + # exit code 1 = is SSD + + # Ignore Synology SSDs/NVMe drives + brand="$(cat /sys/block/"$1"/device/vendor)" + + if grep -q "$1" /proc/mdstat | grep -E 'raid5|raid6'; then + if [[ $show_trim_warning != "yes" && $brand != "Synology" ]]; then + show_trim_warning="yes" + fi + fi + fi +} + +for d in /sys/block/*; do + # $d is /sys/block/sata1 etc + case "$(basename -- "${d}")" in + sd*|hd*) + if [[ $d =~ [hs]d[a-z][a-z]?$ ]]; then + getdriveinfo "$d" + is_ssd "$(basename -- "${d}")" + fi + ;; + sas*) + if [[ $d =~ sas[0-9][0-9]?[0-9]?$ ]]; then + getdriveinfo "$d" + is_ssd "$(basename -- "${d}")" + fi + ;; + sata*) + if [[ $d =~ sata[0-9][0-9]?[0-9]?$ ]]; then + getdriveinfo "$d" + is_ssd "$(basename -- "${d}")" + + # In case it's a SATA M.2 SSD in device tree model NAS + # M.2 SATA drives in M2D18 or M2S17 + m2_drive "$d" + fi + ;; + nvme*) + if [[ $d =~ nvme[0-9][0-9]?n[0-9][0-9]?$ ]]; then + m2_drive "$d" "nvme" + #is_ssd "$(basename -- "${d}")" + fi + ;; + nvc*) # M.2 SATA drives (in PCIe M2D18 or M2S17 only?) + if [[ $d =~ nvc[0-9][0-9]?$ ]]; then + m2_drive "$d" "nvc" + is_ssd "$(basename -- "${d}")" + fi + ;; + esac +done + + +# Sort hdlist array into new hdds array to remove duplicates +if [[ ${#hdlist[@]} -gt "0" ]]; then + while IFS= read -r -d '' x; do + hdds+=("$x") + done < <(printf "%s\0" "${hdlist[@]}" | sort -uz) +fi + +# Show hdds if hdds array isn't empty +if [[ ${#hdds[@]} -eq "0" ]]; then + echo -e "No SATA or SAS drives found\n" +else + echo -e "\nHDD/SSD models found: ${#hdds[@]}" + num="0" + while [[ $num -lt "${#hdds[@]}" ]]; do + echo "${hdds[num]} GB" + num=$((num +1)) + done echo fi @@ -562,7 +1252,7 @@ if [[ ${#nvmelist[@]} -gt "0" ]]; then done < <(printf "%s\0" "${nvmelist[@]}" | sort -uz) fi -# Check nvmes array isn't empty +# Show nvmes if nvmes array isn't empty if [[ $m2 != "no" ]]; then if [[ ${#nvmes[@]} -eq "0" ]]; then echo -e "No M.2 drives found\n" @@ -571,7 +1261,7 @@ if [[ $m2 != "no" ]]; then echo "M.2 drive models found: ${#nvmes[@]}" num="0" while [[ $num -lt "${#nvmes[@]}" ]]; do - echo "${nvmes[num]}" + echo "${nvmes[num]} GB" num=$((num +1)) done echo @@ -579,6 +1269,13 @@ if [[ $m2 != "no" ]]; then fi +# Exit if no drives found +if [[ ${#hdds[@]} -eq "0" && ${#nvmes[@]} -eq "0" ]]; then + ding + echo -e "\n${Error}ERROR${Off} No drives found!" && exit 2 +fi + + # M.2 card db files # Sort m2carddblist array into new m2carddbs array to remove duplicates if [[ ${#m2carddblist[@]} -gt "0" ]]; then @@ -598,9 +1295,9 @@ fi # Check m2cards array isn't empty if [[ $m2 != "no" ]]; then if [[ ${#m2cards[@]} -eq "0" ]]; then - echo -e "No M.2 cards found\n" + echo -e "No M.2 PCIe cards found\n" else - echo "M.2 card models found: ${#m2cards[@]}" + echo "M.2 PCIe card models found: ${#m2cards[@]}" num="0" while [[ $num -lt "${#m2cards[@]}" ]]; do echo "${m2cards[num]}" @@ -612,12 +1309,28 @@ fi # Expansion units -# Get list of connected expansion units (aka eunit/ebox) -path="/var/log/diskprediction" -# shellcheck disable=SC2012 -file=$(ls $path | tail -n1) -# shellcheck disable=SC2207 -eunitlist=($(grep -Eow "([FRD]XD?[0-9]{3,4})(RP|II|sas){0,2}" "$path/$file" | uniq)) +ebox_conected=$(synodisk --enum -t ebox) +if [[ $ebox_conected ]]; then + # Only device tree models have syno_slot_mapping + # eSATA and InfiniBand ports both appear in syno_slot_mapping as: + # Esata port count: 1 + # Eunit port 1 - RX1214 + if which syno_slot_mapping >/dev/null; then + # syno_slot_mapping does not find SAS eunits + eunitlist=($(syno_slot_mapping | grep 'Eunit port' | awk '{print $5}')) + fi + if [[ ${#eunitlist[@]} -eq "0" ]]; then + # Create new /var/log/diskprediction log to ensure newly connected ebox is in latest log + # Otherwise the new /var/log/diskprediction log is only created a midnight. + /usr/syno/bin/syno_disk_data_collector record + + # Get list of connected expansion units (aka eunit/ebox) + path="/var/log/diskprediction" + # shellcheck disable=SC2012 + file=$(ls $path | tail -n1) + eunitlist=($(grep -Eowi "([FRD]XD?[0-9]{3,4})(rp|ii|sas){0,2}" "$path/$file" | uniq)) + fi +fi # Sort eunitlist array into new eunits array to remove duplicates if [[ ${#eunitlist[@]} -gt "0" ]]; then @@ -644,141 +1357,353 @@ fi #------------------------------------------------------------------------------ # Check databases and add our drives if needed -db1="/var/lib/disk-compatibility/${model}_host${version}.db" -db2="/var/lib/disk-compatibility/${model}_host${version}.db.new" -dbpath="/var/lib/disk-compatibility/" +# Host db files +db1list=($(find "$dbpath" -maxdepth 1 -name "*_host*.db")) +db2list=($(find "$dbpath" -maxdepth 1 -name "*_host*.db.new")) +#db1list=($(find "$dbpath" -maxdepth 1 -regextype posix-extended\ +# -iregex ".*_host(_v7)?.db")) +#db2list=($(find "$dbpath" -maxdepth 1 -regextype posix-extended\ +# -iregex ".*_host(_v7)?.db.new")) + +# Expansion Unit db files +for i in "${!eunits[@]}"; do + #eunitdb1list+=($(find "$dbpath" -maxdepth 1 -name "${eunits[i],,}*.db")) + eunitdb1list+=($(find "$dbpath" -maxdepth 1 -regextype posix-extended\ + -iregex ".*${eunits[i],,}(_v7)?.db")) + #eunitdb2list+=($(find "$dbpath" -maxdepth 1 -name "${eunits[i],,}*.db.new")) + eunitdb2list+=($(find "$dbpath" -maxdepth 1 -regextype posix-extended\ + -iregex ".*${eunits[i],,}(_v7)?.db.new")) +done -synoinfo="/etc.defaults/synoinfo.conf" +# M.2 Card db files +for i in "${!m2cards[@]}"; do + m2carddb1list+=($(find "$dbpath" -maxdepth 1 -name "*_${m2cards[i],,}*.db")) + m2carddb2list+=($(find "$dbpath" -maxdepth 1 -name "*_${m2cards[i],,}*.db.new")) +done -if [[ ! -f "$db1" ]]; then echo -e "${Error}ERROR 3${Off} $db1 not found!" && exit 3; fi -#if [[ ! -f "$db2" ]]; then echo -e "${Error}ERROR 4${Off} $db2 not found!" && exit 4; fi -# new installs don't have a .db.new file +if [[ ${#db1list[@]} -eq "0" ]]; then + ding + echo -e "${Error}ERROR 4${Off} Host db file not found!" && exit 4 +fi +# Don't check .db.new as new installs don't have a .db.new file -getdbtype(){ +getdbtype(){ # Detect drive db type - if grep -F '{"disk_compatbility_info":' "$1" >/dev/null; then + # Synology misspelt compatibility as compatbility + if grep -q -F '{"disk_compatbility_info":' "$1"; then # DSM 7 drive db files start with {"disk_compatbility_info": dbtype=7 - elif grep -F '{"success":1,"list":[' "$1" >/dev/null; then + elif grep -q -F '{"success":1,"list":[' "$1"; then # DSM 6 drive db files start with {"success":1,"list":[ dbtype=6 + elif [[ ! $1 =~ .*'.db.new' ]]; then + if [[ $(stat -c%s "$1") -eq "0" ]]; then + echo -e "${Error}ERROR${Off} $(basename -- "${1}") is 0 bytes!" >&2 + else + echo -e "${Error}ERROR${Off} Unknown database type $(basename -- "${1}")!" >&2 + fi + dbtype=1 else - echo -e "${Error}ERROR${Off} Unknown database type $(basename -- "${1}")!" >&2 dbtype=1 fi #echo "db type: $dbtype" >&2 # debug } -backupdb() { +backupdb(){ # Backup database file if needed if [[ ! -f "$1.bak" ]]; then if [[ $(basename "$1") == "synoinfo.conf" ]]; then - echo "" >&2 + echo "" >&2 # Formatting for stdout fi - if cp "$1" "$1.bak"; then - echo -e "Backed up $(basename -- "${1}")" >&2 + if [[ $2 == "long" ]]; then + fname="$1" else - echo -e "${Error}ERROR 5${Off} Failed to backup $(basename -- "${1}")!" >&2 + fname=$(basename -- "${1}") + fi + if cp -p "$1" "$1.bak"; then + echo -e "Backed up ${fname}" >&2 + else + echo -e "${Error}ERROR 5${Off} Failed to backup ${fname}!" >&2 + return 1 + fi + fi + # Fix permissions if needed + octal=$(stat -c "%a %n" "$1" | cut -d" " -f1) + if [[ ! $octal -eq 644 ]]; then + chmod 644 "$1" + fi + return 0 +} + + +compactdb(){ + # Compact database file if needed + local lines + lines=$(wc -l "$1" | cut -d" " -f1) + if (grep -q ': ' "$1" && grep -q ', ' "$1") || [[ $lines -gt 1 ]]; then + # DSM 7.3 and later or pretty-print style + cp -p "$1" "$1.tmp" && jq -c . "$1.tmp" > "$1" && chmod 644 "$1" && rm "$1.tmp" + + # Check database is now compact + lines=$(wc -l "$1" | cut -d" " -f1) + if (grep -q ': ' "$1" && grep -q ', ' "$1") || [[ $lines -gt 1 ]]; then + echo -e "${Error}ERROR${Off} Failed to compact ${1##*/}!" >&2 return 1 + else + return 0 fi fi } +# Python based function to remove duplicates using EOF method +check_and_merge_dupes(){ + local file="$1" + local count + local i + local array + + [[ -z "$file" ]] && { echo "ERROR No file specified"; return 1; } + + # Detect duplicate 2nd-level keys (drive models) in json (db) file + # jq removes the duplicates so we start with an array of drive models + readarray -t array < <(jq . "$file" | grep -e '^ "' | cut -d'"' -f2) + + # Loop through array of drives in db file checking for duplicates + for i in "${array[@]}"; do + # Check if more than 1 of each drive in db file + #count=$(grep -Eo "\"$i\"" "$file" | wc -l) + count=$(grep -Foc "$i" "$file") + if [[ $count -gt 1 ]]; then + + # Python 3 and 2.7 compatible HERE document + python < /dev/null; then + echo -e "Added ${Yellow}$hdmodel ($fwrev)${Off} to ${Cyan}$(basename -- "$2")${Off}" + editcount "$2" + else + echo -e "\n${Error}ERROR{Off} Failed to add $hdmodel ($fwrev) to $(basename -- "$2")${Off}" + fi else - echo -e "\n${Error}ERROR 6a${Off} Failed to update $(basename -- "$2")${Off}" + echo -e "\n${Error}ERROR 6a${Off} Failed to add $hdmodel ($fwrev) to $(basename -- "$2")${Off}" #exit 6 fi elif [[ $1 == "insert" ]]; then # model and default exists - if sed -i "s/\"$hdmodel\":{/\"$hdmodel\":{$fwstrng/" "$2"; then # insert firmware - echo -e "Updated ${Yellow}$hdmodel${Off} to ${Cyan}$(basename -- "$2")${Off}" - #editcount "$2" + #if sed -i "s/\"$hdmodel\":{/\"$hdmodel\":{$fwstrng/" "$2"; then # insert firmware + if sed -i "s/\"${hdmodel//\//\\/}\":{/\"${hdmodel//\//\\/}\":{$fwstrng/" "$2"; then # insert firmware + if jq -e --arg hdmodel "$hdmodel" --arg fwrev "$fwrev" \ + '.disk_compatbility_info[$hdmodel] | has($fwrev)' "$2" > /dev/null; then + echo -e "Updated ${Yellow}$hdmodel ($fwrev)${Off} in ${Cyan}$(basename -- "$2")${Off}" + #editcount "$2" + else + echo -e "\n${Error}ERROR{Off} Failed to update $hdmodel for ($fwrev) in $(basename -- "$2")" + fi else - echo -e "\n${Error}ERROR 6b${Off} Failed to update $(basename -- "$2")${Off}" + echo -e "\n${Error}ERROR 6b${Off} Failed to update $hdmodel for ($fwrev) in $(basename -- "$2")" #exit 6 fi elif [[ $1 == "empty" ]]; then # db file only contains {} - if sed -i "s/{}/{\"$hdmodel\":{$fwstrng${default}}/" "$2"; then # empty - echo -e "Added ${Yellow}$hdmodel${Off} to ${Cyan}$(basename -- "$2")${Off}" - editcount "$2" + #if sed -i "s/{}/{\"$hdmodel\":{$fwstrng${default}}/" "$2"; then # empty + #if sed -i "s/{}/{\"${hdmodel//\//\\/}\":{$fwstrng${default}}/" "$2"; then # empty + if sed -i "s/{}/{\"${hdmodel//\//\\/}\":{$fwstrng${default}/" "$2"; then # empty + if jq -e --arg hdmodel "$hdmodel" --arg fwrev "$fwrev" \ + '.disk_compatbility_info[$hdmodel] | has($fwrev)' "$2" > /dev/null; then + echo -e "Added ${Yellow}$hdmodel ($fwrev)${Off} to ${Cyan}$(basename -- "$2")${Off}" + editcount "$2" + else + echo -e "\n${Error}ERROR{Off} Failed to add $hdmodel ($fwrev) to $(basename -- "$2")" + fi else - echo -e "\n${Error}ERROR 6c${Off} Failed to update $(basename -- "$2")${Off}" + echo -e "\n${Error}ERROR 6c${Off} Failed to add $hdmodel ($fwrev) to $(basename -- "$2")" #exit 6 fi - fi } -updatedb() { +updatedb(){ hdmodel=$(printf "%s" "$1" | cut -d"," -f 1) fwrev=$(printf "%s" "$1" | cut -d"," -f 2) + size_gb=$(printf "%s" "$1" | cut -d"," -f 3) #echo arg1 "$1" >&2 # debug #echo arg2 "$2" >&2 # debug #echo hdmodel "$hdmodel" >&2 # debug #echo fwrev "$fwrev" >&2 # debug - if grep "$hdmodel"'":{"'"$fwrev" "$2" >/dev/null; then - echo -e "${Yellow}$hdmodel${Off} already exists in ${Cyan}$(basename -- "$2")${Off}" >&2 - else - # Check if db file is new or old style - getdbtype "$2" - - if [[ $dbtype -gt "6" ]]; then - # Don't need to add firmware version? - fwstrng=\"$fwrev\" - fwstrng="$fwstrng":{\"compatibility_interval\":[{\"compatibility\":\"support\",\"not_yet_rolling_status\" - fwstrng="$fwstrng":\"support\",\"fw_dsm_update_status_notify\":false,\"barebone_installable\":true}]}, - - default=\"default\" - default="$default":{\"compatibility_interval\":[{\"compatibility\":\"support\",\"not_yet_rolling_status\" - default="$default":\"support\",\"fw_dsm_update_status_notify\":false,\"barebone_installable\":true}]}}} + # Check if db file is new or old style + getdbtype "$2" - if grep '"disk_compatbility_info":{}' "$2" >/dev/null; then - # Replace "disk_compatbility_info":{} with "disk_compatbility_info":{"WD40PURX-64GVNY0":{"80.00A80":{ ... }}},"default":{ ... }}}} - echo "Edit empty db file" + if [[ $dbtype -gt "6" ]]; then + # db type 7 used from DSM 7.1 and later + if jq -e --arg hdmodel "$hdmodel" --arg fwrev "$fwrev" \ + '.disk_compatbility_info[$hdmodel] | has($fwrev)' "$2" > /dev/null; then + echo -e "${Yellow}$hdmodel ($fwrev)${Off} already exists in ${Cyan}$(basename -- "$2")${Off}" >&2 + else + #common_string=\"size_gb\":$size_gb, + #common_string="$common_string"\"compatibility_interval\":[{ + common_string=\"compatibility_interval\":[{ + common_string="$common_string"\"compatibility\":\"support\", + common_string="$common_string"\"not_yet_rolling_status\":\"support\", + common_string="$common_string"\"fw_dsm_update_status_notify\":false, + common_string="$common_string"\"barebone_installable\":true, + common_string="$common_string"\"barebone_installable_v2\":\"auto\", + common_string="$common_string"\"smart_test_ignore\":false, + common_string="$common_string"\"smart_attr_ignore\":false + + fwstrng=\"$fwrev\":{ + fwstrng="$fwstrng$common_string" + fwstrng="$fwstrng"}]}, + + #default=\"default\":{ + default=\"default\":{\"size_gb\":$size_gb, + default="$default$common_string" + default="$default"}]}}} + + # Synology misspelt compatibility as compatbility + if grep -q '"disk_compatbility_info":{}' "$2"; then + # Replace "disk_compatbility_info":{} with + # "disk_compatbility_info":{"WD40PURX-64GVNY0":{"80.00A80":{ ... }}},"default":{ ... }}}} + #echo "Edit empty db file:" # debug editdb7 "empty" "$2" - elif grep '"'"$hdmodel"'":' "$2" >/dev/null; then - # Replace "WD40PURX-64GVNY0":{ with "WD40PURX-64GVNY0":{"80.00A80":{ ... }}}, - echo "Insert firmware version" + elif jq -e --arg hdmodel "$hdmodel" '.disk_compatbility_info[$hdmodel]' "$2" >/dev/null; then + # Replace "WD40PURX-64GVNY0":{ with "WD40PURX-64GVNY0":{"80.00A80":{ ... }}}, + #echo "Insert firmware version:" # debug editdb7 "insert" "$2" else - # Add "WD40PURX-64GVNY0":{"80.00A80":{ ... }}},"default":{ ... }}} - echo "Append drive and firmware" + # Add "WD40PURX-64GVNY0":{"80.00A80":{ ... }}},"default":{ ... }}} + #echo "Append drive and firmware:" # debug editdb7 "append" "$2" fi + fi - elif [[ $dbtype -eq "6" ]]; then + # Edit existing drives in db with compatibility:unverified # Issue #224 + if grep -q 'unverified' "$2"; then + sed -i 's/unverified/support/g' "$2" + if ! grep -q 'unverified' "$2"; then + echo -e "Edited unverified drives in ${Cyan}$(basename -- "$2")${Off}" >&2 + fi + fi + + # Edit existing drives in db with compatibility:not_support + if [[ $incompatible == "yes" ]]; then + if grep -q 'not_support' "$2"; then + sed -i 's/not_support/support/g' "$2" + if ! grep -q 'not_support' "$2"; then + echo -e "Edited incompatible drives in ${Cyan}$(basename -- "$2")${Off}" >&2 + fi + fi + fi + elif [[ $dbtype -eq "6" ]]; then + # db type 6 used up to DSM 7.0.1 + if grep -q "$hdmodel" "$2"; then + echo -e "${Yellow}$hdmodel${Off} already exists in ${Cyan}$(basename -- "$2")${Off}" >&2 + else # example: # {"model":"WD60EFRX-68MYMN1","firmware":"82.00A82","rec_intvl":[1]}, # Don't need to add firmware version? @@ -786,24 +1711,14 @@ updatedb() { string="{\"model\":\"${hdmodel}\",\"firmware\":\"\",\"rec_intvl\":\[1\]}," # {"success":1,"list":[ startstring="{\"success\":1,\"list\":\[" - - #echo "$startstring" >&2 # debug - #echo "$string" >&2 # debug - #echo >&2 # debug - # example: # {"success":1,"list":[{"model":"WD60EFRX-68MYMN1","firmware":"82.00A82","rec_intvl":[1]}, - if sed -ir "s/$startstring/$startstring$string/" "$2"; then - echo -e "Added ${Yellow}$hdmodel${Off} to ${Cyan}$(basename -- "$2")${Off}" - - # Count drives added to host db files - if [[ $2 == "$db1" ]]; then - db1Edits=$((db1Edits +1)) - elif [[ $2 == "$db2" ]]; then - db2Edits=$((db2Edits +1)) - fi - + #if sed -i "s/$startstring/$startstring$string/" "$2"; then + #if sed -i "s/${startstring//\//\\/}/${startstring//\//\\/}$string/" "$2"; then + if sed -i "s/$startstring/$startstring${string//\//\\/}/" "$2"; then + echo -e "Added ${Yellow}$hdmodel$ ($fwrev){Off} to ${Cyan}$(basename -- "$2")${Off}" else + ding echo -e "\n${Error}ERROR 8${Off} Failed to update $(basename -- "$2")${Off}" >&2 exit 8 fi @@ -811,26 +1726,42 @@ updatedb() { fi } + +# Fix ,, instead of , bug caused by v3.3.75 +if [[ "${#db1list[@]}" -gt "0" ]]; then + for i in "${!db1list[@]}"; do + sed -i "s/,,/,/" "${db1list[i]}" + done +fi +if [[ "${#db2list[@]}" -gt "0" ]]; then + for i in "${!db2list[@]}"; do + sed -i "s/,,/,/" "${db2list[i]}" + done +fi + # HDDs and SATA SSDs num="0" while [[ $num -lt "${#hdds[@]}" ]]; do - updatedb "${hdds[$num]}" "$db1" - if [[ -f "$db2" ]]; then - updatedb "${hdds[$num]}" "$db2" - fi + for i in "${!db1list[@]}"; do + updatedb "${hdds[$num]}" "${db1list[i]}" + done + for i in "${!db2list[@]}"; do + updatedb "${hdds[$num]}" "${db2list[i]}" + done #------------------------------------------------ # Expansion Units - num2="0" - while [[ $num2 -lt "${#eunits[@]}" ]]; do - eudb="${dbpath}${eunits[$num2],,}${version}.db" - if [[ -f "$eudb" ]]; then - backupdb "$eudb" &&\ - updatedb "${hdds[$num]}" "$eudb" - else - echo -e "${Error}ERROR 11${Off} $eudb not found!" - fi - num2=$((num2 +1)) + for i in "${!eunitdb1list[@]}"; do + backupdb "${eunitdb1list[i]}" &&\ + compactdb "${eunitdb1list[i]}" &&\ + check_and_merge_dupes "${eunitdb1list[i]}" &&\ + updatedb "${hdds[$num]}" "${eunitdb1list[i]}" + done + for i in "${!eunitdb2list[@]}"; do + backupdb "${eunitdb2list[i]}" &&\ + compactdb "${eunitdb2list[i]}" &&\ + check_and_merge_dupes "${eunitdb2list[i]}" &&\ + updatedb "${hdds[$num]}" "${eunitdb2list[i]}" done #------------------------------------------------ @@ -840,22 +1771,26 @@ done # M.2 NVMe/SATA drives num="0" while [[ $num -lt "${#nvmes[@]}" ]]; do - updatedb "${nvmes[$num]}" "$db1" - if [[ -f "$db2" ]]; then - updatedb "${nvmes[$num]}" "$db2" - fi + for i in "${!db1list[@]}"; do + updatedb "${nvmes[$num]}" "${db1list[i]}" + done + for i in "${!db2list[@]}"; do + updatedb "${nvmes[$num]}" "${db2list[i]}" + done #------------------------------------------------ # M.2 adaptor cards - num2="0" - while [[ $num2 -lt "${#m2carddbs[@]}" ]]; do - if [[ -f "${dbpath}${m2carddbs[$num2]}" ]]; then - backupdb "${dbpath}${m2carddbs[$num2]}" &&\ - updatedb "${nvmes[$num]}" "${dbpath}${m2carddbs[$num2]}" - else - echo -e "${Error}ERROR 10${Off} ${m2carddbs[$num2]} not found!" - fi - num2=$((num2 +1)) + for i in "${!m2carddb1list[@]}"; do + backupdb "${m2carddb1list[i]}" &&\ + compactdb "${m2carddb1list[i]}" &&\ + check_and_merge_dupes "${m2carddb1list[i]}" &&\ + updatedb "${nvmes[$num]}" "${m2carddb1list[i]}" + done + for i in "${!m2carddb2list[@]}"; do + backupdb "${m2carddb2list[i]}" &&\ + compactdb "${m2carddb2list[i]}" &&\ + check_and_merge_dupes "${m2carddb2list[i]}" &&\ + updatedb "${nvmes[$num]}" "${m2carddb2list[i]}" done #------------------------------------------------ @@ -863,173 +1798,812 @@ while [[ $num -lt "${#nvmes[@]}" ]]; do done +#------------------------------------------------------------------------------ +# Enable unsupported Synology M2 PCIe cards + +enable_card(){ + # $1 is the file + # $2 is the section + # $3 is the card model and mode + if [[ -f $1 && -n $2 && -n $3 ]]; then + backupdb "$adapter_cards" long + backupdb "$adapter_cards2" long + + # Check if section exists + if ! grep -q '^\['"$2"'\]$' "$1"; then + echo -e "Section [$2] not found in $(basename -- "$1")!" >&2 + return + fi + # Check if already enabled + # + # No idea if "cat /proc/sys/kernel/syno_hw_version" returns upper or lower case RP + # "/usr/syno/etc.defaults/adapter_cards.conf" uses lower case rp but upper case RS + # So we'll convert RP to rp when needed. + # + modelrplowercase=${modelname//RP/rp} + val=$(/usr/syno/bin/get_section_key_value "$1" "$2" "$modelrplowercase") + if [[ $val != "yes" ]]; then + # /usr/syno/etc.defaults/adapter_cards.conf + if /usr/syno/bin/set_section_key_value "$1" "$2" "$modelrplowercase" yes; then + # /usr/syno/etc/adapter_cards.conf + /usr/syno/bin/set_section_key_value "$adapter_cards2" "$2" "$modelrplowercase" yes + echo -e "Enabled ${Yellow}$3${Off} for ${Cyan}$modelname${Off}" >&2 + rebootmsg=yes + else + echo -e "${Error}ERROR 9${Off} Failed to enable $3 for ${modelname}!" >&2 + fi + else + echo -e "${Yellow}$3${Off} already enabled for ${Cyan}$modelname${Off}" >&2 + fi + fi +} + +dts_m2_card(){ +# $1 is the card model +# $2 is the dts file + +# Remove last }; so we can append to dts file +sed -i '/^};/d' "$2" + +# Append PCIe M.2 card node to dts file +if [[ $1 == E10M20-T1 || $1 == M2D20 ]]; then + cat >> "$2" <> "$2" <; + }; + + nvme { + pcie_postfix = "00.0,04.0,00.0"; + port_type = "ssdcache"; + }; + }; + + m2_card@2 { + + ahci { + pcie_postfix = "00.0,03.0,00.0"; + ata_port = <0x01>; + }; + + nvme { + pcie_postfix = "00.0,05.0,00.0"; + port_type = "ssdcache"; + }; + }; + }; +}; +EOM2D18 + +elif [[ $1 == M2D17 ]]; then + cat >> "$2" <; + }; + }; + + m2_card@2 { + + ahci { + pcie_postfix = "00.0,03.0,00.0"; + ata_port = <0x01>; + }; + }; + }; +}; +EOM2D17 + +fi +} + +is_schedule_running(){ + # $1 is script's filename. e.g. syno_hdd_db.sh etc + local file="/usr/syno/etc/esynoscheduler/esynoscheduler.db" + local rows offset task status pid result + + # Get number of rows in database + rows=$(sqlite3 "${file}" < + # example: + # file_url="https://raw.githubusercontent.com/${repo}/main/bin/dtc" + # install_binfile dtc "$file_url" /usr/bin/dtc a+x bin/dtc + + if [[ -f "${scriptpath}/$5" ]]; then + binfile="${scriptpath}/$5" + echo -e "\nInstalling ${1}" + elif [[ -f "${scriptpath}/$(basename -- "$5")" ]]; then + binfile="${scriptpath}/$(basename -- "$5")" + echo -e "\nInstalling ${1}" + else + # Download binfile + if [[ $autoupdate == "yes" ]]; then + reply=y + elif is_schedule_running "$(basename -- "$0")"; then + reply=y + else + echo -e "\nNeed to download ${1}" + echo -e "${Cyan}Do you want to download ${1}?${Off} [y/n]" + read -r -t 30 reply + fi + if [[ ${reply,,} == "y" ]]; then + echo -e "\nDownloading ${1}" + if ! curl -kL -m 30 --connect-timeout 5 "$2" -o "/tmp/$1"; then + echo -e "${Error}ERROR${Off} Failed to download ${1}!" + return + fi + binfile="/tmp/${1}" + + printf "Downloaded md5: " + md5sum -b "$binfile" | awk '{print $1}' + + md5=$(md5sum -b "$binfile" | awk '{print $1}') + if [[ $md5 != "$6" ]]; then + echo "Expected md5: $6" + echo -e "${Error}ERROR${Off} Downloaded $1 md5 hash does not match!" + exit 1 + fi + else + echo -e "${Error}ERROR${Off} Cannot add M2 PCIe card without ${1}!" + exit 1 + fi + fi + + # Set binfile executable + chmod "$4" "$binfile" + + # Copy binfile to destination + cp -p "$binfile" "$3" +} + +edit_modeldtb(){ + # $1 is E10M20-T1 or M2D20 or M2D18 or M2D17 + if [[ -f /etc.defaults/model.dtb ]]; then # Is device tree model + # Check if dtc exists and is executable + if [[ ! -x $(which dtc) ]]; then + md5hash="01381dabbe86e13a2f4a8017b5552918" + branch="main" + file_url="https://raw.githubusercontent.com/${repo}/${branch}/bin/dtc" + # install_binfile + install_binfile dtc "$file_url" /usr/sbin/dtc "a+x" bin/dtc "$md5hash" + fi + + # Check again if dtc exists and is executable + if [[ -x /usr/sbin/dtc ]]; then + + # Backup model.dtb + backupdb "$dtb_file" long + + # Output model.dtb to model.dts + dtc -q -I dtb -O dts -o "$dts_file" "$dtb_file" # -q Suppress warnings + chmod 644 "$dts_file" + + # Edit model.dts + for c in "${cards[@]}"; do + # Edit model.dts if needed + if ! grep -q "$c" "$dtb_file"; then + dts_m2_card "$c" "$dts_file" + echo -e "Added ${Yellow}$c${Off} to ${Cyan}model${hwrev}.dtb${Off}" >&2 + else + echo -e "${Yellow}$c${Off} already exists in ${Cyan}model${hwrev}.dtb${Off}" >&2 + fi + done + + # Compile model.dts to model.dtb + dtc -q -I dts -O dtb -o "$dtb_file" "$dts_file" # -q Suppress warnings + + # Set owner and permissions for model.dtb + chmod a+r "$dtb_file" + chown root:root "$dtb_file" + cp -pu "$dtb_file" "$dtb2_file" # Copy dtb file to /etc + rebootmsg=yes + else + echo -e "${Error}ERROR${Off} Missing /usr/sbin/dtc or not executable!" >&2 + fi + fi +} + + +for c in "${m2cards[@]}"; do + case "$c" in + E10M20-T1) + echo "" + enable_card "$adapter_cards" E10M20-T1_sup_nic "E10M20-T1 NIC" + enable_card "$adapter_cards" E10M20-T1_sup_nvme "E10M20-T1 NVMe" + #enable_card "$adapter_cards" E10M20-T1_sup_sata "E10M20-T1 SATA" + cards=(E10M20-T1) && edit_modeldtb + ;; + M2D20) + echo "" + enable_card "$adapter_cards" M2D20_sup_nvme "M2D20 NVMe" + cards=(M2D20) && edit_modeldtb + ;; + M2D18) + echo "" + enable_card "$adapter_cards" M2D18_sup_nvme "M2D18 NVMe" + enable_card "$adapter_cards" M2D18_sup_sata "M2D18 SATA" + cards=(M2D18) && edit_modeldtb + ;; + M2D17) + echo "" + enable_card "$adapter_cards" M2D17_sup_sata "M2D17 SATA" + cards=(M2D17) && edit_modeldtb + ;; + *) + echo "Unknown M2 card type: $c" + ;; + esac +done + + +#------------------------------------------------------------------------------ +# Set or restore writemostly + +if [[ $ssd == "yes" ]]; then + # Get array of internal drives + readarray -t internal_drives < <(synodisk --enum -t internal | grep 'Disk path' | cut -d"/" -f3) + + if [[ $ssd_restore == "yes" ]]; then + # Restore all internal drives to just in_sync + echo -e "\nRestoring internal drive's state" + for idrive in "${internal_drives[@]}"; do + #if ! grep -q "write_mostly"; then + set_writemostly -writemostly "$idrive" + #fi + done + + elif [[ ${#ssds_writemostly[@]} -gt "0" ]]; then + # User specified their fast drive(s) + echo -e "\nSetting slow internal HDDs state to write_mostly" + for idrive in "${internal_drives[@]}"; do + if [[ ! ${ssds_writemostly[*]} =~ $idrive ]]; then + set_writemostly writemostly "$idrive" + fi + done + + else + # Get list of internal HDDs and qty of SSDs + internal_ssd_qty="0" + for idrive in "${internal_drives[@]}"; do + if synodisk --isssd /dev/"${idrive:?}" >/dev/null; then + # exit code 0 = is not SSD + # exit code 1 = is SSD + + # Add internal HDDs to array + internal_hdds+=("$idrive") + else + # Count number of internal 2.5 inch SSDs + internal_ssd_qty=$((internal_ssd_qty +1)) + fi + done + + # Set HDDs to writemostly if there's also internal SSDs + if [[ $internal_ssd_qty -gt "0" && ${#internal_hdds[@]} -gt "0" ]]; then + # There are internal SSDs and HDDs + echo -e "\nSetting internal HDDs state to write_mostly" + for idrive in "${internal_hdds[@]}"; do + set_writemostly writemostly "$idrive" + done + fi + fi +fi + + #------------------------------------------------------------------------------ # Edit /etc.defaults/synoinfo.conf # Backup synoinfo.conf if needed -backupdb "$synoinfo" || exit 9 +backupdb "$synoinfo" ||{ + ding + exit 9 +} # Optionally disable "support_disk_compatibility" sdc=support_disk_compatibility -setting="$(get_key_value $synoinfo $sdc)" +setting="$(/usr/syno/bin/synogetkeyvalue $synoinfo $sdc)" if [[ $force == "yes" ]]; then if [[ $setting == "yes" ]]; then # Disable support_disk_compatibility - #sed -i "s/${sdc}=\"yes\"/${sdc}=\"no\"/" "$synoinfo" - synosetkeyvalue "$synoinfo" "$sdc" "no" - setting="$(get_key_value "$synoinfo" $sdc)" + /usr/syno/bin/synosetkeyvalue "$synoinfo" "$sdc" "no" + setting="$(/usr/syno/bin/synogetkeyvalue "$synoinfo" $sdc)" if [[ $setting == "no" ]]; then echo -e "\nDisabled support disk compatibility." fi + elif [[ $setting == "no" ]]; then + echo -e "\nSupport disk compatibility already disabled." fi else if [[ $setting == "no" ]]; then # Enable support_disk_compatibility - #sed -i "s/${sdc}=\"no\"/${sdc}=\"yes\"/" "$synoinfo" - synosetkeyvalue "$synoinfo" "$sdc" "yes" - setting="$(get_key_value "$synoinfo" $sdc)" + /usr/syno/bin/synosetkeyvalue "$synoinfo" "$sdc" "yes" + setting="$(/usr/syno/bin/synogetkeyvalue "$synoinfo" $sdc)" if [[ $setting == "yes" ]]; then echo -e "\nRe-enabled support disk compatibility." fi + elif [[ $setting == "yes" ]]; then + echo -e "\nSupport disk compatibility already enabled." fi fi -# Optionally disable "support_memory_compatibility" +# Optionally disable memory compatibility warnings smc=support_memory_compatibility -setting="$(get_key_value $synoinfo $smc)" -if [[ $ram == "yes" ]]; then - if [[ $setting == "yes" ]]; then - # Disable support_memory_compatibility - #sed -i "s/${smc}=\"yes\"/${smc}=\"no\"/" "$synoinfo" - synosetkeyvalue "$synoinfo" "$smc" "no" - setting="$(get_key_value "$synoinfo" $smc)" - if [[ $setting == "no" ]]; then - echo -e "\nDisabled support memory compatibility." +setting="$(/usr/syno/bin/synogetkeyvalue $synoinfo $smc)" +settingbak="$(/usr/syno/bin/synogetkeyvalue $synoinfo.bak $smc)" + +if [[ -z $settingbak || -z $setting ]]; then + # For older models that don't use "support_memory_compatibility" + memcheck="/usr/lib/systemd/system/SynoMemCheck.service" + memcheck_value="$(/usr/syno/bin/synosetkeyvalue "$memcheck" ExecStart)" + if [[ $ram == "yes" ]]; then + if [[ $memcheck_value == "/usr/syno/bin/syno_mem_check" ]]; then + # Disable SynoMemCheck.service + /usr/syno/bin/synosetkeyvalue "$memcheck" ExecStart /bin/true + memcheck_value="$(/usr/syno/bin/synosetkeyvalue "$memcheck" ExecStart)" + if [[ $memcheck_value == "/bin/true" ]]; then + echo -e "\nDisabled SynoMemCheck memory compatibility." + fi + elif [[ $memcheck_value == "/bin/true" ]]; then + echo -e "\nSynoMemCheck memory compatibility already disabled." + fi + else + if [[ $memcheck_value == "/bin/true" ]]; then + # Enable SynoMemCheck.service + /usr/syno/bin/synosetkeyvalue "$memcheck" ExecStart /usr/syno/bin/syno_mem_check + memcheck_value="$(/usr/syno/bin/synosetkeyvalue "$memcheck" ExecStart)" + if [[ $memcheck_value == "/usr/syno/bin/syno_mem_check" ]]; then + echo -e "\nRe-enabled SynoMemCheck memory compatibility." + fi + elif [[ $memcheck_value == "/usr/syno/bin/syno_mem_check" ]]; then + echo -e "\nSynoMemCheck memory compatibility already enabled." fi fi else - if [[ $setting == "no" ]]; then - # Enable support_memory_compatibility - #sed -i "s/${smc}=\"no\"/${smc}=\"yes\"/" "$synoinfo" - synosetkeyvalue "$synoinfo" "$smc" "yes" - setting="$(get_key_value "$synoinfo" $smc)" + # Disable "support_memory_compatibility" (not for older models) + if [[ $ram == "yes" ]]; then if [[ $setting == "yes" ]]; then - echo -e "\nRe-enabled support memory compatibility." + # Disable support_memory_compatibility + /usr/syno/bin/synosetkeyvalue "$synoinfo" "$smc" "no" + setting="$(/usr/syno/bin/synogetkeyvalue "$synoinfo" $smc)" + if [[ $setting == "no" ]]; then + echo -e "\nDisabled support memory compatibility." + fi + elif [[ $setting == "no" ]]; then + echo -e "\nSupport memory compatibility already disabled." + fi + else + if [[ $setting == "no" ]]; then + # Enable support_memory_compatibility + /usr/syno/bin/synosetkeyvalue "$synoinfo" "$smc" "yes" + setting="$(/usr/syno/bin/synogetkeyvalue "$synoinfo" $smc)" + if [[ $setting == "yes" ]]; then + echo -e "\nRe-enabled support memory compatibility." + fi + elif [[ $setting == "yes" ]]; then + echo -e "\nSupport memory compatibility already enabled." fi fi fi -# Optioanlly set mem_max_mb to the amount of installed memory -if [[ $ram == "yes" ]]; then - # Get total amount of installed memory - IFS=$'\n' read -r -d '' -a array < <(dmidecode -t memory | grep -i 'size') - if [[ ${#array[@]} -gt "0" ]]; then - num="0" - while [[ $num -lt "${#array[@]}" ]]; do - ramsize=$(printf %s "${array[num]}" | cut -d" " -f2) - if [[ $ramtotal ]]; then - ramtotal=$((ramtotal +ramsize)) - else - ramtotal="$ramsize" +# Optionally set mem_max_mb to the amount of installed memory +if [[ $dsm -gt "6" ]]; then # DSM 6 as has no dmidecode + if [[ $ram == "yes" && -f /usr/sbin/dmidecode ]]; then + # Get total amount of installed memory + #IFS=$'\n' read -r -d '' -a array < <(dmidecode -t memory | grep "[Ss]ize") # GitHub issue #86, 87 + IFS=$'\n' read -r -d '' -a array < <(dmidecode -t memory |\ + grep -E "[Ss]ize: [0-9]+ [MG]{1}[B]{1}$") # GitHub issue #86, 87, 106 + if [[ ${#array[@]} -gt "0" ]]; then + num="0" + while [[ $num -lt "${#array[@]}" ]]; do + check=$(printf %s "${array[num]}" | awk '{print $1}') + if [[ ${check,,} == "size:" ]]; then + ramsize=$(printf %s "${array[num]}" | awk '{print $2}') # GitHub issue #86, 87 + bytes=$(printf %s "${array[num]}" | awk '{print $3}') # GitHub issue #86, 87 + if [[ $ramsize =~ ^[0-9]+$ ]]; then # Check $ramsize is numeric # GitHub issue #86, 87 + if [[ $bytes == "GB" ]]; then # DSM 7.2 dmidecode returned GB + ramsize=$((ramsize * 1024)) # Convert to MB # GitHub issue #107 + fi + if [[ $ramtotal ]]; then + ramtotal=$((ramtotal +ramsize)) + else + ramtotal="$ramsize" + fi + fi + fi + num=$((num +1)) + done + fi + # Set mem_max_mb to the amount of installed memory + setting="$(/usr/syno/bin/synogetkeyvalue $synoinfo mem_max_mb)" + settingbak="$(/usr/syno/bin/synogetkeyvalue ${synoinfo}.bak mem_max_mb)" # GitHub issue #107 + if [[ $ramtotal =~ ^[0-9]+$ ]]; then # Check $ramtotal is numeric + if [[ $ramtotal -gt "$setting" ]]; then + /usr/syno/bin/synosetkeyvalue "$synoinfo" mem_max_mb "$ramtotal" + # Check we changed mem_max_mb + setting="$(/usr/syno/bin/synogetkeyvalue $synoinfo mem_max_mb)" + if [[ $ramtotal == "$setting" ]]; then + #echo -e "\nSet max memory to $ramtotal MB." + ramgb=$((ramtotal / 1024)) + echo -e "\nSet max memory to $ramgb GB." + else + echo -e "\n${Error}ERROR${Off} Failed to change max memory!" + fi + + elif [[ $setting -gt "$ramtotal" && $setting -gt "$settingbak" ]]; # GitHub issue #107 + then + # Fix setting is greater than both ramtotal and default in syninfo.conf.bak + /usr/syno/bin/synosetkeyvalue "$synoinfo" mem_max_mb "$settingbak" + # Check we restored mem_max_mb + setting="$(/usr/syno/bin/synogetkeyvalue $synoinfo mem_max_mb)" + if [[ $settingbak == "$setting" ]]; then + #echo -e "\nSet max memory to $ramtotal MB." + ramgb=$((ramtotal / 1024)) + echo -e "\nRestored max memory to $ramgb GB." + else + echo -e "\n${Error}ERROR${Off} Failed to restore max memory!" + fi + + elif [[ $ramtotal == "$setting" ]]; then + #echo -e "\nMax memory already set to $ramtotal MB." + ramgb=$((ramtotal / 1024)) + echo -e "\nMax memory already set to $ramgb GB." + else [[ $ramtotal -lt "$setting" ]] + #echo -e "\nMax memory is set to $setting MB." + ramgb=$((setting / 1024)) + echo -e "\nMax memory is set to $ramgb GB." fi - num=$((num +1)) - done - fi - # Set mem_max_mb to the amount of installed memory - setting="$(get_key_value $synoinfo mem_max_mb)" - if [[ $ramtotal -gt $setting ]]; then - synosetkeyvalue "$synoinfo" mem_max_mb "$ramtotal" - # Check we changed mem_max_mb - setting="$(get_key_value $synoinfo mem_max_mb)" - if [[ $setting == "$ramtotal" ]]; then - echo -e "\nSet max memory to $ramtotal MB." else - echo -e "\n${Error}ERROR${Off} Failed to change max memory!" + echo -e "\n${Error}ERROR${Off} Total memory size is not numeric: '$ramtotal'" fi - elif [[ $setting == "$ramtotal" ]]; then - echo -e "\nMax memory already set to $ramtotal MB." fi fi -# Enable m2 volume support -if [[ $m2 != "no" ]]; then - if [[ $m2exists == "yes" ]]; then - # Check if m2 volume support is enabled - smp=support_m2_pool - setting="$(get_key_value $synoinfo ${smp})" +# Enable nvme support +# shellcheck disable=SC2010 # Don't warn about "Don't use ls | grep" +if ls /dev | grep -q nvme; then + if [[ $m2 != "no" ]]; then + # Check if nvme support is enabled + setting="$(/usr/syno/bin/synogetkeyvalue $synoinfo supportnvme)" enabled="" if [[ ! $setting ]]; then - # Add support_m2_pool="yes" - echo 'support_m2_pool="yes"' >> "$synoinfo" + # Add supportnvme="yes" + /usr/syno/bin/synosetkeyvalue "$synoinfo" supportnvme "yes" enabled="yes" elif [[ $setting == "no" ]]; then - # Change support_m2_pool="no" to "yes" - #sed -i "s/${smp}=\"no\"/${smp}=\"yes\"/" "$synoinfo" - synosetkeyvalue "$synoinfo" "$smp" "yes" + # Change supportnvme="no" to "yes" + /usr/syno/bin/synosetkeyvalue "$synoinfo" supportnvme "yes" enabled="yes" elif [[ $setting == "yes" ]]; then - echo -e "\nM.2 volume support already enabled." + echo -e "\nNVMe support already enabled." fi - # Check if we enabled m2 volume support - setting="$(get_key_value $synoinfo ${smp})" + # Check if we enabled nvme support + setting="$(/usr/syno/bin/synogetkeyvalue $synoinfo supportnvme)" if [[ $enabled == "yes" ]]; then if [[ $setting == "yes" ]]; then - echo -e "\nEnabled M.2 volume support." + echo -e "\nEnabled NVMe support." else - echo -e "\n${Error}ERROR${Off} Failed to enable m2 volume support!" + echo -e "\n${Error}ERROR${Off} Failed to enable NVMe support!" fi fi fi fi -# Edit synoinfo.conf to prevent drive db updates -dtu=drive_db_test_url -url="$(get_key_value $synoinfo ${dtu})" -disabled="" -if [[ $nodbupdate == "yes" ]]; then - if [[ ! $url ]]; then - # Add drive_db_test_url="127.0.0.1" - echo 'drive_db_test_url="127.0.0.1"' >> "$synoinfo" - disabled="yes" - elif [[ $url != "127.0.0.1" ]]; then - # Edit drive_db_test_url= - #sed -i "s/drive_db_test_url=.*/drive_db_test_url=\"127.0.0.1\"/" "$synoinfo" >/dev/null - synosetkeyvalue "$synoinfo" "$dtu" "127.0.0.1" - disabled="yes" +# Enable m2 volume support +# shellcheck disable=SC2010 # Don't warn about "Don't use ls | grep" +if ls /dev | grep -q "nv[cm]"; then + if [[ $m2 != "no" ]]; then + if [[ $m2exists == "yes" ]]; then + # Check if m2 volume support is enabled + smp=support_m2_pool + setting="$(/usr/syno/bin/synogetkeyvalue $synoinfo ${smp})" + enabled="" + if [[ ! $setting ]]; then + # Add support_m2_pool="yes" + #echo 'support_m2_pool="yes"' >> "$synoinfo" + /usr/syno/bin/synosetkeyvalue "$synoinfo" "$smp" "yes" + enabled="yes" + elif [[ $setting == "no" ]]; then + # Change support_m2_pool="no" to "yes" + /usr/syno/bin/synosetkeyvalue "$synoinfo" "$smp" "yes" + enabled="yes" + elif [[ $setting == "yes" ]]; then + echo -e "\nM.2 volume support already enabled." + fi + + # Check if we enabled m2 volume support + setting="$(/usr/syno/bin/synogetkeyvalue $synoinfo ${smp})" + if [[ $enabled == "yes" ]]; then + if [[ $setting == "yes" ]]; then + echo -e "\nEnabled M.2 volume support." + else + echo -e "\n${Error}ERROR${Off} Failed to enable m2 volume support!" + fi + fi + fi fi +fi - # Check if we disabled drive db auto updates - url="$(get_key_value $synoinfo drive_db_test_url)" - if [[ $disabled == "yes" ]]; then - if [[ $url == "127.0.0.1" ]]; then - echo -e "\nDisabled drive db auto updates." + +# Prevent drive db updates +if [[ $dsmversion -lt "73" ]]; then + # Edit synoinfo.conf to prevent drive db updates + dtu=drive_db_test_url + url="$(/usr/syno/bin/synogetkeyvalue $synoinfo ${dtu})" + disabled="" + if [[ $nodbupdate == "yes" ]]; then + if [[ ! $url ]]; then + # Add drive_db_test_url="127.0.0.1" + #echo 'drive_db_test_url="127.0.0.1"' >> "$synoinfo" + /usr/syno/bin/synosetkeyvalue "$synoinfo" "$dtu" "127.0.0.1" + # Junior boot + #[ -d /tmpRoot ] && /tmpRoot/usr/syno/bin/synosetkeyvalue /tmpRoot/etc.defaults/synoinfo.conf "$dtu" "127.0.0.1" + if [ -f /tmpRoot/usr/syno/bin/synosetkeyvalue ] && [ -f /tmpRoot/etc.defaults/synoinfo.conf ]; then + /tmpRoot/usr/syno/bin/synosetkeyvalue /tmpRoot/etc.defaults/synoinfo.conf "$dtu" "127.0.0.1" + fi + disabled="yes" + elif [[ $url != "127.0.0.1" ]]; then + # Edit drive_db_test_url= + /usr/syno/bin/synosetkeyvalue "$synoinfo" "$dtu" "127.0.0.1" + # Junior boot + #[ -d /tmpRoot ] && /tmpRoot/usr/syno/bin/synosetkeyvalue /tmpRoot/etc.defaults/synoinfo.conf "$dtu" "127.0.0.1" + if [ -f /tmpRoot/usr/syno/bin/synosetkeyvalue ] && [ -f /tmpRoot/etc.defaults/synoinfo.conf ]; then + /tmpRoot/usr/syno/bin/synosetkeyvalue /tmpRoot/etc.defaults/synoinfo.conf "$dtu" "127.0.0.1" + fi + disabled="yes" + fi + + # Check if we disabled drive db auto updates + url="$(/usr/syno/bin/synogetkeyvalue $synoinfo drive_db_test_url)" + if [[ $disabled == "yes" ]]; then + if [[ $url == "127.0.0.1" ]]; then + echo -e "\nDisabled drive db auto updates." + else + echo -e "\n${Error}ERROR${Off} Failed to disable drive db auto updates!" + fi else - echo -e "\n${Error}ERROR${Off} Failed to disable drive db auto updates!" + echo -e "\nDrive db auto updates already disabled." fi else - echo -e "\nDrive db auto updates already disabled." + # Re-enable drive db updates + #if [[ $url == "127.0.0.1" ]]; then + if [[ $url ]]; then + # Delete "drive_db_test_url=127.0.0.1" line (inc. line break) + sed -i "/drive_db_test_url=*/d" "$synoinfo" + sed -i "/drive_db_test_url=*/d" /etc/synoinfo.conf + + # Check if we re-enabled drive db auto updates + url="$(/usr/syno/bin/synogetkeyvalue $synoinfo drive_db_test_url)" + if [[ $url != "127.0.0.1" ]]; then + echo -e "\nRe-enabled drive db auto updates." + else + echo -e "\n${Error}ERROR${Off} Failed to enable drive db auto updates!" + fi + else + echo -e "\nDrive db auto updates already enabled." + fi fi else - # Re-enable drive db updates - #if [[ $url == "127.0.0.1" ]]; then - if [[ $url ]]; then - # Delete "drive_db_test_url=127.0.0.1" line (inc. line break) - #sed -i "/drive_db_test_url=\"127.0.0.1\"/d" "/etc.defaults/synoinfo.conf" - sed -i "/drive_db_test_url=*/d" "/etc.defaults/synoinfo.conf" - - # Check if we re-enabled drive db auto updates - url="$(get_key_value $synoinfo drive_db_test_url)" - if [[ $url != "127.0.0.1" ]]; then - echo -e "\nRe-enabled drive db auto updates." + # Is DSM 7.3 or later + if [[ -f /var/packages/SynoOnlinePack_v3/INFO ]]; then + SOPinfo="/var/packages/SynoOnlinePack_v3/INFO" + elif [[ -f /var/packages/SynoOnlinePack_v2/INFO ]]; then + SOPinfo="/var/packages/SynoOnlinePack_v2/INFO" + else + SOPinfo="/var/packages/SynoOnlinePack/INFO" + fi + SOPpkgver="$(/usr/syno/bin/synogetkeyvalue $SOPinfo version)" + + if [[ $nodbupdate == "yes" ]]; then + if [[ ${SOPpkgver:0:4} != "9999" ]]; then + # Prepend version with 9999 + /usr/syno/bin/synosetkeyvalue "$SOPinfo" version "9999$SOPpkgver" + disabled="yes" + fi + + # Check if we disabled drive db auto updates + SOPpkgver2="$(/usr/syno/bin/synogetkeyvalue $SOPinfo version)" + if [[ $disabled == "yes" ]]; then + if [[ $SOPpkgver2 -gt "$SOPpkgver" ]]; then + echo -e "\nDisabled drive db auto updates." + else + echo -e "\n${Error}ERROR${Off} Failed to disable drive db auto updates!" + fi else - echo -e "\n${Error}ERROR${Off} Failed to enable drive db auto updates!" + echo -e "\nDrive db auto updates already disabled." fi else - echo -e "\nDrive db auto updates already enabled." + # Re-enable drive db updates + if [[ ${SOPpkgver:0:4} == "9999" ]]; then + # Remove 9999 from version + /usr/syno/bin/synosetkeyvalue "$SOPinfo" version "${SOPpkgver:4}" + + # Check if we re-enabled drive db auto updates + SOPpkgver2="$(/usr/syno/bin/synogetkeyvalue $SOPinfo version)" + if [[ ${SOPpkgver2:0:4} != "9999" ]]; then + echo -e "\nRe-enabled drive db auto updates." + else + echo -e "\n${Error}ERROR${Off} Failed to enable drive db auto updates!" + fi + else + echo -e "\nDrive db auto updates already enabled." + fi + fi +fi + + +# Optionally disable "support_wdda" +setting="$(/usr/syno/bin/synogetkeyvalue $synoinfo support_wdda)" +if [[ $wdda == "no" ]]; then + if [[ $setting == "yes" ]]; then + # Disable support_wdda + /usr/syno/bin/synosetkeyvalue "$synoinfo" support_wdda "no" + setting="$(/usr/syno/bin/synogetkeyvalue "$synoinfo" support_wdda)" + if [[ $setting == "no" ]]; then + echo -e "\nDisabled support WDDA." + fi + elif [[ $setting == "no" ]]; then + echo -e "\nSupport WDDA already disabled." + fi +fi + + +# Enable creating pool on drives in M.2 adaptor card +if [[ -f "$strgmgr" && $buildnumber -gt 42962 ]]; then + # DSM 7.1.1 and later + if [[ ${#m2cards[@]} -gt "0" || $forcepci == "yes" ]]; then + + if grep -q 'notSupportM2Pool_addOnCard' "$strgmgr"; then + # Backup storage_panel.js" + + if [[ $buildnumber -gt 64570 ]]; then + # DSM 7.2.1 and later + strgmgrver="$(/usr/syno/bin/synopkg version StorageManager)" + elif [[ $buildnumber -ge 42962 ]]; then + # DSM 7.1.1 to 7.2 + strgmgrver="${buildnumber}${smallfixnumber}" + fi + + echo "" + if [[ ! -f "${strgmgr}.$strgmgrver" ]]; then + if cp -p "$strgmgr" "${strgmgr}.$strgmgrver"; then + echo -e "Backed up $(basename -- "$strgmgr")" + else + echo -e "${Error}ERROR${Off} Failed to backup $(basename -- "$strgmgr")!" + fi + fi + + sed -i 's/notSupportM2Pool_addOnCard:this.T("disk_info","disk_reason_m2_add_on_card"),//g' "$strgmgr" + sed -i 's/},{isConditionInvalid:0 + install_binfile dhm_tool "$file_url" /usr/syno/sbin/dhm_tool "755" bin/dhm_tool "$md5hash" + else + # Check if dhm_tool needs updating + dhm_version="$(dhm_tool --version | grep "Utility Version" | awk '{print $NF}')" + if ! printf "%s\n%s\n" "2.5.1" "$dhm_version" | + sort --check=quiet --version-sort >/dev/null ; then + + # Backup existing dhm_tool + backupdb "/usr/syno/sbin/dhm_tool" + + # Update dhm_tool + md5hash="cf67c1d5006913297f85ca7f9d1795ba" + branch="main" + file_url="https://raw.githubusercontent.com/${repo}/${branch}/bin/dhm_tool" + # install_binfile + install_binfile dhm_tool "$file_url" /usr/syno/sbin/dhm_tool "755" bin/dhm_tool "$md5hash" + + # Check dhm_tool updated + dhm_version="$(dhm_tool --version | grep "Utility Version" | awk '{print $NF}')" + if [[ $dhm_version == "2.5.1" ]]; then + echo "Updated IronWolf Health Management." + else + echo "${Error}ERROR${Off} Failed to update IronWolf Health Management!" + fi + else + echo "IronWolf Health Management already updated." + fi + fi fi fi @@ -1037,61 +2611,76 @@ fi #------------------------------------------------------------------------------ # Finished +show_changes(){ + # $1 is drive_model,firmware_version,size_gb + drive_model="$(printf "%s" "$1" | cut -d"," -f 1)" + echo -e "\n$drive_model:" + jq -r --arg drive_model "$drive_model" '.disk_compatbility_info[$drive_model]' "${db1list[0]}" +} + # Show the changes if [[ ${showedits,,} == "yes" ]]; then - getdbtype "$db1" - if [[ $dbtype -gt "6" ]]; then - # Show 11 lines after hdmodel line - lines=11 - elif [[ $dbtype -eq "6" ]]; then - # Show 2 lines after hdmodel line - lines=2 - fi - # HDDs/SSDs - if [[ ${#hdds[@]} -gt "0" ]]; then - num="0" - while [[ $num -lt "${#hdds[@]}" ]]; do - hdmodel=$(printf "%s" "${hdds[$num]}" | cut -d"," -f 1) - echo - jq . "$db1" | grep -A "$lines" "$hdmodel" - num=$((num +1)) - done - fi + for d in "${hdds[@]}"; do + show_changes "$d" + done # NVMe drives - if [[ ${#nvmes[@]} -gt "0" ]]; then - num="0" - while [[ $num -lt "${#nvmes[@]}" ]]; do - nvmemodel=$(printf "%s" "${nvmes[$num]}" | cut -d"," -f 1) - echo - jq . "$db1" | grep -A "$lines" "$nvmemodel" - num=$((num +1)) - done - fi - + for d in "${nvmes[@]}"; do + show_changes "$d" + done fi - # Make Synology check disk compatibility if [[ -f /usr/syno/sbin/synostgdisk ]]; then # DSM 6.2.3 does not have synostgdisk /usr/syno/sbin/synostgdisk --check-all-disks-compatibility status=$? if [[ $status -eq "0" ]]; then echo -e "\nDSM successfully checked disk compatibility." + rebootmsg=yes # Show reboot message at end else # Ignore DSM 6.2.4 as it returns 255 for "synostgdisk --check-all-disks-compatibility" + # and DSM 6.2.3 and lower have no synostgdisk command if [[ $dsm -gt "6" ]]; then echo -e "\nDSM ${Red}failed${Off} to check disk compatibility with exit code $status" - echo -e "\nYou may need to ${Cyan}reboot the Synology${Off} to see the changes." + rebootmsg=yes # Show reboot message at end fi fi fi -if [[ $dsm -eq "6" ]]; then - echo -e "\nYou may need to ${Cyan}reboot the Synology${Off} to see the changes." +# Enable creating M.2 storage pool and volume in Storage Manager +for d in /sys/block/nvme*; do + # $d is /sys/block/nvme0n1 etc + if [[ $d =~ nvme[0-9][0-9]?n[0-9][0-9]?$ ]]; then + m2_pool_support "$d" + fi +done + +# Show TRIM warning if required +if [[ $show_trim_warning == "yes" ]]; then + ding + echo -e "\n${Warn}WARNING${Off} Enabling SSD TRIM on drives in RAID 5, 6 or SHR with 3 more drives can" + echo "result in data loss if the SSD/NVMe drives marks trimmed blocks as released." + echo "SSDs that use Method 1 are okay. Do NOT enable TRIM for SSDs that use Method 2." + echo "See Why_is_SSD_TRIM_available_only_for_SSDs_in_the_compatibility_list here:" + echo "https://tinyurl.com/ssd-trim" fi +# Show reboot message or reboot cleanly once if needed +if [[ $do_reboot == "yes" && $sch_task == "yes" ]];then + # Reboot cleanly after DSM update if needed + previous_build="$(synogetkeyvalue "$reboot_file" dsm_build)" + if [[ $buildnumber -gt "$previous_build" ]]; then + synosetkeyvalue "$reboot_file" dsm_build "$buildnumber" # Update buildnumber + echo -e "\nDSM has updated from build $previous_build to $buildnumber" + echo "Rebooting..." + synoshutdown --reboot # Reboot cleanly + exit + fi +elif [[ $dsm -eq "6" || $rebootmsg == "yes" ]]; then + # Show reboot message if required + echo -e "\nYou may need to ${Cyan}reboot the Synology${Off} to see the changes." +fi exit diff --git a/syno_hdd_shutdown.sh b/syno_hdd_shutdown.sh new file mode 100644 index 0000000..40ea1e2 --- /dev/null +++ b/syno_hdd_shutdown.sh @@ -0,0 +1,411 @@ +#!/usr/bin/env bash +# shellcheck disable=SC2002,2207 +#------------------------------------------------------------------------------------ +# Companion script for syno_hdd_db +# Schedule companion script to run as root at shut-down +# +# https://www.synology-forum.de/threads/neue-version-7-3-2-86009.140586/post-1265124 +#------------------------------------------------------------------------------------ +# TODO +# Check running on DSM version that needs it +# What to do if @database is on the NVMe volume? + +scriptver="v1.0.0" +script=Synology_HDD_shutdown +#repo="007revad/Synology_HDD_db" +scriptname=syno_hdd_shutdown + +ding(){ + printf \\a +} + +# Save options used for getopt +args=("$@") + +if [[ $1 == "--trace" ]] || [[ $1 == "-t" ]]; then + trace="yes" +fi + +# Check script is running as root +if [[ $( whoami ) != "root" ]]; then + ding + echo -e "ERROR This script must be run as sudo or root!" + exit 1 # Not running as sudo or root +fi + +# Get NAS model +model=$(cat /proc/sys/kernel/syno_hw_version) +#modelname="$model" + + +# Show script version +#echo -e "$script $scriptver\ngithub.com/$repo\n" +echo "$script $scriptver" + +# Get DSM full version +productversion=$(synogetkeyvalue /etc.defaults/VERSION productversion) +buildphase=$(synogetkeyvalue /etc.defaults/VERSION buildphase) +buildnumber=$(synogetkeyvalue /etc.defaults/VERSION buildnumber) +smallfixnumber=$(synogetkeyvalue /etc.defaults/VERSION smallfixnumber) + +# Get CPU arch and platform_name +arch="$(uname -m)" +platform_name=$(synogetkeyvalue /etc.defaults/synoinfo.conf platform_name) + +# Show DSM full version and model +if [[ $buildphase == GM ]]; then buildphase=""; fi +if [[ $smallfixnumber -gt "0" ]]; then smallfix="-$smallfixnumber"; fi +echo "$model DSM $productversion-$buildnumber$smallfix $buildphase" + +# Show CPU arch and platform_name +echo "CPU $platform_name $arch" + +# Show options used +if [[ ${#args[@]} -gt "0" ]]; then + echo -e "Using options: ${args[*]}\n" +else + echo "" +fi + +# Check Synology has synonvme +if ! which synonvme >/dev/null; then + ding + echo "${model} does not have synonvme!" + exit 2 # NAS model does support NVMe +fi + +# Check Synology has libsynonvme.so.1 +if [[ ! -e /usr/lib/libsynonvme.so ]]; then + ding + echo "${model} does not have libsynonvme.so.1!" + exit 2 # NAS model does support NVMe +fi + +#set -x + +# Get script location +# https://stackoverflow.com/questions/59895/ +source=${BASH_SOURCE[0]} +while [ -L "$source" ]; do # Resolve $source until the file is no longer a symlink + scriptpath=$( cd -P "$( dirname "$source" )" >/dev/null 2>&1 && pwd ) + source=$(readlink "$source") + # If $source was a relative symlink, we need to resolve it + # relative to the path where the symlink file was located + [[ $source != /* ]] && source=$scriptpath/$source +done +scriptpath=$( cd -P "$( dirname "$source" )" >/dev/null 2>&1 && pwd ) +scriptfile=$( basename -- "$source" ) +echo "Running from: ${scriptpath}/$scriptfile" + +# Warn if script located on M.2 drive +scriptvol=$(echo "$scriptpath" | cut -d"/" -f2) +vg=$(lvdisplay | grep /volume_"${scriptvol#volume}" | cut -d"/" -f3) +md=$(pvdisplay | grep -B 1 -E '[ ]'"$vg" | grep /dev/ | cut -d"/" -f3) +if cat /proc/mdstat | grep "$md" | grep nvme >/dev/null; then + ding + echo -e "WARNING Don't store this script on an NVMe volume!" + exit 3 # Script is stored on NVMe volume +fi + +shutdown_log="${scriptpath}/${scriptname}.log" +# Delete old shutdown_log +if [[ -f "$shutdown_log" ]]; then + rm "$shutdown_log" +fi + +progbar(){ + # $1 is pid of process + # $2 is string to echo + string="$2" + local dots + local progress + dots="" + while [[ -d /proc/$1 ]]; do + dots="${dots}." + progress="$dots" + if [[ ${#dots} -gt "10" ]]; then + dots="" + progress=" " + fi + echo -ne " ${2}$progress\r"; /usr/bin/sleep 0.3 + done +} + +progstatus(){ + # $1 is return status of process + # $2 is string to echo + # $3 line number function was called from + local tracestring + local pad + tracestring="${FUNCNAME[0]} called from ${FUNCNAME[1]} $3" + pad=$(printf -- ' %.0s' {1..80}) + [ "$trace" == "yes" ] && printf '%.*s' 80 "${tracestring}${pad}" && echo "" + if [[ $1 == "0" ]]; then + echo -e "$2 " + else + ding + echo -e "Line ${LINENO}: ERROR $2 failed!" + echo "$tracestring ($scriptver)" + if [[ $exitonerror != "no" ]]; then + exit 1 # Skip exit if exitonerror != no + fi + fi + exitonerror="" + #echo "return: $1" # debug +} + +package_status(){ + # $1 is package name + [ "$trace" == "yes" ] && echo "${FUNCNAME[0]} called from ${FUNCNAME[1]}" +# local code + synopkg status "${1}" >/dev/null + code="$?" + # DSM 7.2 0 = started, 17 = stopped, 255 = not_installed, 150 = broken + # DSM 6 to 7.1 0 = started, 3 = stopped, 4 = not_installed, 150 = broken + if [[ $code == "0" ]]; then + #echo "$1 is started" # debug + return 0 + elif [[ $code == "17" ]] || [[ $code == "3" ]]; then + #echo "$1 is stopped" # debug + return 1 + elif [[ $code == "255" ]] || [[ $code == "4" ]]; then + #echo "$1 is not installed" # debug + return 255 + elif [[ $code == "150" ]]; then + #echo "$1 is broken" # debug + return 150 + else + return "$code" + fi +} + +package_is_running(){ + # $1 is package name + [ "$trace" == "yes" ] && echo "${FUNCNAME[0]} called from ${FUNCNAME[1]}" + synopkg is_onoff "${1}" >/dev/null + code="$?" + return "$code" +} + +wait_status(){ + # Wait for package to finish stopping or starting + # $1 is package + # $2 is start or stop + [ "$trace" == "yes" ] && echo "${FUNCNAME[0]} called from ${FUNCNAME[1]}" + local num + if [[ $2 == "start" ]]; then + state="0" + elif [[ $2 == "stop" ]]; then + state="1" + fi + if [[ $state == "0" ]] || [[ $state == "1" ]]; then + num="0" + package_status "$1" + while [[ $? != "$state" ]]; do + sleep 1 + num=$((num +1)) + if [[ $num -gt "20" ]]; then + break + fi + package_status "$1" + done + fi +} + +package_stop(){ + # $1 is package name + # $2 is package display name + [ "$trace" == "yes" ] && echo "${FUNCNAME[0]} called from ${FUNCNAME[1]}" + # Docker can take 12 minutes to stop 70 containers + timeout 30m synopkg stop "$1" >/dev/null & + pid=$! + string="Stopping ${2}" + progbar "$pid" "$string" + wait "$pid" + progstatus "$?" "$string" "line ${LINENO}" + + # Allow package processes to finish stopping + wait_status "$1" stop & + pid=$! + string="Waiting for ${2} to stop" + progbar "$pid" "$string" + wait "$pid" + progstatus "$?" "$string" "line ${LINENO}" +} + +stop_packages(){ + # Check package is running + [ "$trace" == "yes" ] && echo "${FUNCNAME[0]} called from ${FUNCNAME[1]}" + if package_is_running "$pkg"; then + + # Stop package + package_stop "$pkg" "$pkg_name" + + # Check package stopped + if package_is_running "$pkg"; then + #stop_pkg_fail="yes" + ding + echo -e "Line ${LINENO}: ERROR Failed to stop ${pkg_name}!" +# echo "${pkg_name} status $code" + #process_error="yes" + return 1 + else + echo "$pkg" >> "$shutdown_log" + #stop_pkg_fail="" + fi + + if [[ $pkg == "ContainerManager" ]] || [[ $pkg == "Docker" ]]; then + # Stop containerd-shim + killall containerd-shim >/dev/null 2>&1 + fi +# else +# skip_start="yes" + fi +} + +skip_dev_tools(){ + # $1 is $package + [ "$trace" == "yes" ] && echo "${FUNCNAME[0]} called from ${FUNCNAME[1]}" + local skip1 + local skip2 + skip1="$(synogetkeyvalue "/var/packages/${package}/INFO" startable)" + skip2="$(synogetkeyvalue "/var/packages/${package}/INFO" ctl_stop)" + if [[ $skip1 == "no" ]] || [[ $skip2 == "no" ]]; then + return 0 + else + return 1 + fi +} + + +# Get list of volumes with shared folders +readarray -t shares_array < <(synoshare --enum | tail -n +5) +for share in "${shares_array[@]}"; do + +#echo "share: $share" # debug ################################################ + + if [[ $buildnumber -gt "64570" ]]; then + # DSM 7.2.1 and later + # synoshare --get-real-path is case insensitive + path="$(synoshare --get-real-path "$share" | cut -d"/" -f2)" + else + # DSM 7.2 and earlier + # synoshare --getmap is case insensitive + path="$(synoshare --getmap "$share" | grep volume | cut -d"[" -f2 | cut -d"]" -f1)" + # I could also have used: + # web_pkg_path=$(/usr/syno/sbin/synoshare --get "$share" | tr '[]' '\n' | sed -n "9p") + fi + + # Ignore external USB and eSATA volumes + if echo "$path" | grep -q -E 'volume[0-9]'; then + volumes_array+=("$path") + fi +done + +#echo -e "\nvolumes_array:" # debug ################## +#for v in "${volumes_array[@]}"; do echo "$v"; done # debug ################## +#echo -e "\n" # debug ################## + +# Sort array to remove duplicates +IFS=$'\n' +volumes_array_sorted=($(sort -u <<<"${volumes_array[*]}")) +unset IFS + +#echo -e "\nvolumes_array_sorted:" # debug ########### +#for v in "${volumes_array_sorted[@]}"; do echo "$v"; done # debug ########### +#echo -e "\n" # debug ########### + +#exit + +# Check there is an NVMe volume +nvme_vols=() +for vol in "${volumes_array_sorted[@]}"; do + +#echo "$vol" # debug ######################################################### + + vg=$(lvdisplay | grep /volume_"${vol#volume}" | cut -d"/" -f3) + md=$(pvdisplay | grep -B 1 -E '[ ]'"$vg" | grep /dev/ | cut -d"/" -f3) + if cat /proc/mdstat | grep "$md" | grep nvme >/dev/null; then + nvme_vols_qty=$((nvme_vols_qty +1)) + nvme_vols+=("$vol") + fi +done +if [[ $nvme_vols_qty -lt "0" ]]; then + ding + echo -e "No NVMe volumes found." + exit 4 # Script not needed +fi + + +#echo "nvme_vols_qty $nvme_vols_qty" # debug ###################### +#for v in "${nvme_vols[@]}"; do echo "$v"; done # debug ###################### +#echo "" # debug ###################### + +#exit + + +# Get list of packages installed on NVMe volume +if ! cd /var/packages; then + ding + echo -e "Failed to cd to /var/packages!" + exit 5 # Failed to cd to /var/packages +fi +declare -A package_names +declare -A package_names_rev +#package_infos=( ) +while IFS= read -r -d '' link && IFS= read -r -d '' target; do + if [[ ${link##*/} == "target" ]] && echo "$target" | grep -q 'volume'; then + # Check symlink target exists + if [[ -a "/var/packages${link#.}" ]] ; then + + # Skip broken packages with no INFO file + package="$(printf %s "$link" | cut -d'/' -f2 )" + if [[ -f "/var/packages/${package}/INFO" ]]; then + package_volume="$(printf %s "$target" | cut -d'/' -f1,2 )" + +#echo "package_volume: $package_volume" # debug ############################### + + # Check if package is on NVMe volume + # shellcheck disable=SC2076 + if [[ "/${nvme_vols[*]}" =~ "$package_volume" ]]; then + package_name="$(synogetkeyvalue "/var/packages/${package}/INFO" displayname)" + if [[ -z "$package_name" ]]; then + package_name="$(synogetkeyvalue "/var/packages/${package}/INFO" package)" + fi + +#echo "package_name: $package_name" # debug ############################### + + # Skip packages that are dev tools with no data + if ! skip_dev_tools "$package"; then + #package_infos+=("${package_volume}|${package_name}") + package_names["${package_name}"]="${package}" + package_names_rev["${package}"]="${package_name}" + fi + fi + fi + fi + fi +done < <(find . -maxdepth 2 -type l -printf '%p\0%l\0') + +#echo -e "\npackage_infos:" +#for p in "${package_infos[@]}"; do echo "$p"; done + +#echo -e "\npackage_names:" +#for p in "${package_names[@]}"; do echo "$p"; done + +#echo -e "\npackage_names_rev:" +#for p in "${package_names_rev[@]}"; do echo "$p"; done + + +# Loop through pkgs_sorted array and process package +for pkg in "${package_names[@]}"; do + pkg_name="${package_names_rev["$pkg"]}" + #process_error="" + stop_packages + +#echo "stop_package: $pkg" # debug ############################################ + +done + + + diff --git a/syno_hdd_vendor_ids.txt b/syno_hdd_vendor_ids.txt new file mode 100644 index 0000000..9bb8246 --- /dev/null +++ b/syno_hdd_vendor_ids.txt @@ -0,0 +1,24 @@ +# You can add your drive's vendor id and vendor name here. +# +# Only edit this file if the script warns you about a missing vendor id. + +0x025e="Solidigm" +0x05dc="Lexar" +0x0ed1="aigo" +0x10ec="TEAMGROUP" +0x137e="Patriot" +0x1458="Gigabyte" +0x1462="MSI" +0x196e="PNY" +0x1987="Phison" +0x1b1c="Corsair" +0x1bdc="Apacer" +0x1c5c="SK Hynix" +0x1cc4="UMIS" +0x1cfa="Corsair" +0x1d79="Transcend" +0x1dbe="ADATA" +0x1e0f="KIOXIA" +0x1e49="ZHITAI" +0x1e4b="FIKWOT" +0x1f40="Netac"