11.1 Database Discovery. Detect installed database and data services.
11.2 Service Status. Verify systemd service state for detected database services.
11.3 Listening Ports. Identify active database network listeners.
11.4 Installed Versions. Display detected database software versions.
11.5 Database Processes. List running database-related processes and resource usage.
11.6 Configuration Files. Locate default configuration files and directories.
11.7 Storage Assessment. Analyze database storage usage, filesystem status, ownership, permissions, and mount options.
11.8 Log Assessment. Search system and native database logs for recent critical events.
11.9 PostgreSQL Assessment. Perform PostgreSQL-specific read-only diagnostics.
11.10 MySQL / MariaDB Assessment. Perform MySQL and MariaDB-specific read-only diagnostics.
11.11 Redis Assessment. Perform Redis-specific read-only diagnostics.
11.12 Replication & High Availability Assessment. Search for replication, clustering, and high availability indicators.
11.13 Backup Assessment. Detect backup jobs, timers, backup files, and estimate backup freshness.
11.14 Database Health Summary. Present an overall health summary of the database environment.
Why Base64?
Base64 encoding allows complex multi-line Bash scripts to be safely delivered as a single command, avoiding issues with line breaks, quoting, and macro parsing while preserving the original script intact. Use: printf %s <base64_data_ | base64 -d | bash
printf '%s' 'IyEvdXNyL2Jpbi9lbnYgYmFzaAoKIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KIyAxMS4gREFUQUJBU0UgJiBEQVRBIFNFUlZJQ0VTIEFTU0VTU01FTlQKIyBSZWFkLW9ubHkgZGlhZ25vc3RpY3MgZm9yIERlYmlhbiAvIFVidW50dSBzeXN0ZW1zCiMgTm8gcGFzc3dvcmRzIGFyZSByZXF1ZXN0ZWQgYW5kIG5vIGNvbmZpZ3VyYXRpb24gaXMgY2hhbmdlZC4KIyA9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0KCnNldCAtdQpzZXQgLW8gcGlwZWZhaWwKCmlmIFsgLXQgMSBdICYmIFsgIiR7VEVSTTotZHVtYn0iICE9ICJkdW1iIiBdOyB0aGVuCiAgICBHUkVFTj0nXDAzM1swOzMybSc7IFJFRD0nXDAzM1swOzMxbSc7IFlFTExPVz0nXDAzM1sxOzMzbScKICAgIEJMVUU9J1wwMzNbMDszNG0nOyBDWUFOPSdcMDMzWzA7MzZtJzsgTkM9J1wwMzNbMG0nCmVsc2UKICAgIEdSRUVOPScnOyBSRUQ9Jyc7IFlFTExPVz0nJzsgQkxVRT0nJzsgQ1lBTj0nJzsgTkM9JycKZmkKCnByaW50X21haW5faGVhZGVyKCkgeyBwcmludGYgJyViXG5cbicgIiR7QkxVRX09PT09PT09PT09PT09PT09PT09PSAxMS4gREFUQUJBU0UgJiBEQVRBIFNFUlZJQ0VTID09PT09PT09PT09PT09PT09PT09JHtOQ30iOyB9CnByaW50X3NlY3Rpb24oKSB7IHByaW50ZiAnJWJcbicgIiR7QkxVRX0tLS0gJDEgLS0tJHtOQ30iOyB9CnByaW50X29rKCkgeyBwcmludGYgJyViXG4nICIke0dSRUVOfVtPS10ke05DfSAkMSI7IH0KcHJpbnRfaW5mbygpIHsgcHJpbnRmICclYlxuJyAiJHtDWUFOfVtJTkZPXSR7TkN9ICQxIjsgfQpwcmludF93YXJuaW5nKCkgeyBwcmludGYgJyViXG4nICIke1lFTExPV31bV0FSTklOR10ke05DfSAkMSI7IH0KcHJpbnRfY3JpdGljYWwoKSB7IHByaW50ZiAnJWJcbicgIiR7UkVEfVtDUklUSUNBTF0ke05DfSAkMSI7IH0KY29tbWFuZF9leGlzdHMoKSB7IGNvbW1hbmQgLXYgIiQxIiA+L2Rldi9udWxsIDI+JjE7IH0KCkZJTEVTWVNURU1fV0FSTklOR19USFJFU0hPTEQ9ODAKRklMRVNZU1RFTV9DUklUSUNBTF9USFJFU0hPTEQ9OTAKQkFDS1VQX1dBUk5JTkdfREFZUz03CkxPR19MT09LQkFDSz0nMjQgaG91cnMgYWdvJwoKZGVjbGFyZSAtQSBEQl9MQUJFTCBEQl9TRVJWSUNFIERCX0NPTU1BTkQgREJfUE9SVFMgREJfQ09ORklHIERCX0RBVEEgREJfTE9HUwpkZWNsYXJlIC1BIERCX0RFVEVDVEVEIERCX1NUQVRVUyBEQl9WRVJTSU9OCgpEQVRBQkFTRV9LRVlTPShwb3N0Z3Jlc3FsIG15c3FsIG1hcmlhZGIgbW9uZ29kYiByZWRpcyBlbGFzdGljc2VhcmNoIG9wZW5zZWFyY2ggY2xpY2tob3VzZSBpbmZsdXhkYiBjYXNzYW5kcmEgcmFiYml0bXEpCgpEQl9MQUJFTFtwb3N0Z3Jlc3FsXT0nUG9zdGdyZVNRTCc7IERCX1NFUlZJQ0VbcG9zdGdyZXNxbF09J3Bvc3RncmVzcWwnOyBEQl9DT01NQU5EW3Bvc3RncmVzcWxdPSdwc3FsJzsgREJfUE9SVFNbcG9zdGdyZXNxbF09JzU0MzInOyBEQl9DT05GSUdbcG9zdGdyZXNxbF09Jy9ldGMvcG9zdGdyZXNxbCc7IERCX0RBVEFbcG9zdGdyZXNxbF09Jy92YXIvbGliL3Bvc3RncmVzcWwnOyBEQl9MT0dTW3Bvc3RncmVzcWxdPScvdmFyL2xvZy9wb3N0Z3Jlc3FsJwpEQl9MQUJFTFtteXNxbF09J015U1FMJzsgREJfU0VSVklDRVtteXNxbF09J215c3FsJzsgREJfQ09NTUFORFtteXNxbF09J215c3FsJzsgREJfUE9SVFNbbXlzcWxdPSczMzA2JzsgREJfQ09ORklHW215c3FsXT0nL2V0Yy9teXNxbCc7IERCX0RBVEFbbXlzcWxdPScvdmFyL2xpYi9teXNxbCc7IERCX0xPR1NbbXlzcWxdPScvdmFyL2xvZy9teXNxbCcKREJfTEFCRUxbbWFyaWFkYl09J01hcmlhREInOyBEQl9TRVJWSUNFW21hcmlhZGJdPSdtYXJpYWRiJzsgREJfQ09NTUFORFttYXJpYWRiXT0nbWFyaWFkYic7IERCX1BPUlRTW21hcmlhZGJdPSczMzA2JzsgREJfQ09ORklHW21hcmlhZGJdPScvZXRjL215c3FsJzsgREJfREFUQVttYXJpYWRiXT0nL3Zhci9saWIvbXlzcWwnOyBEQl9MT0dTW21hcmlhZGJdPScvdmFyL2xvZy9teXNxbCcKREJfTEFCRUxbbW9uZ29kYl09J01vbmdvREInOyBEQl9TRVJWSUNFW21vbmdvZGJdPSdtb25nb2QnOyBEQl9DT01NQU5EW21vbmdvZGJdPSdtb25nb3NoJzsgREJfUE9SVFNbbW9uZ29kYl09JzI3MDE3JzsgREJfQ09ORklHW21vbmdvZGJdPScvZXRjL21vbmdvZC5jb25mJzsgREJfREFUQVttb25nb2RiXT0nL3Zhci9saWIvbW9uZ29kYic7IERCX0xPR1NbbW9uZ29kYl09Jy92YXIvbG9nL21vbmdvZGInCkRCX0xBQkVMW3JlZGlzXT0nUmVkaXMnOyBEQl9TRVJWSUNFW3JlZGlzXT0ncmVkaXMtc2VydmVyJzsgREJfQ09NTUFORFtyZWRpc109J3JlZGlzLWNsaSc7IERCX1BPUlRTW3JlZGlzXT0nNjM3OSc7IERCX0NPTkZJR1tyZWRpc109Jy9ldGMvcmVkaXMnOyBEQl9EQVRBW3JlZGlzXT0nL3Zhci9saWIvcmVkaXMnOyBEQl9MT0dTW3JlZGlzXT0nL3Zhci9sb2cvcmVkaXMnCkRCX0xBQkVMW2VsYXN0aWNzZWFyY2hdPSdFbGFzdGljc2VhcmNoJzsgREJfU0VSVklDRVtlbGFzdGljc2VhcmNoXT0nZWxhc3RpY3NlYXJjaCc7IERCX0NPTU1BTkRbZWxhc3RpY3NlYXJjaF09J2VsYXN0aWNzZWFyY2gnOyBEQl9QT1JUU1tlbGFzdGljc2VhcmNoXT0nOTIwMCA5MzAwJzsgREJfQ09ORklHW2VsYXN0aWNzZWFyY2hdPScvZXRjL2VsYXN0aWNzZWFyY2gnOyBEQl9EQVRBW2VsYXN0aWNzZWFyY2hdPScvdmFyL2xpYi9lbGFzdGljc2VhcmNoJzsgREJfTE9HU1tlbGFzdGljc2VhcmNoXT0nL3Zhci9sb2cvZWxhc3RpY3NlYXJjaCcKREJfTEFCRUxbb3BlbnNlYXJjaF09J09wZW5TZWFyY2gnOyBEQl9TRVJWSUNFW29wZW5zZWFyY2hdPSdvcGVuc2VhcmNoJzsgREJfQ09NTUFORFtvcGVuc2VhcmNoXT0nb3BlbnNlYXJjaCc7IERCX1BPUlRTW29wZW5zZWFyY2hdPSc5MjAwIDkzMDAnOyBEQl9DT05GSUdbb3BlbnNlYXJjaF09Jy9ldGMvb3BlbnNlYXJjaCc7IERCX0RBVEFbb3BlbnNlYXJjaF09Jy92YXIvbGliL29wZW5zZWFyY2gnOyBEQl9MT0dTW29wZW5zZWFyY2hdPScvdmFyL2xvZy9vcGVuc2VhcmNoJwpEQl9MQUJFTFtjbGlja2hvdXNlXT0nQ2xpY2tIb3VzZSc7IERCX1NFUlZJQ0VbY2xpY2tob3VzZV09J2NsaWNraG91c2Utc2VydmVyJzsgREJfQ09NTUFORFtjbGlja2hvdXNlXT0nY2xpY2tob3VzZS1jbGllbnQnOyBEQl9QT1JUU1tjbGlja2hvdXNlXT0nODEyMyA5MDAwJzsgREJfQ09ORklHW2NsaWNraG91c2VdPScvZXRjL2NsaWNraG91c2Utc2VydmVyJzsgREJfREFUQVtjbGlja2hvdXNlXT0nL3Zhci9saWIvY2xpY2tob3VzZSc7IERCX0xPR1NbY2xpY2tob3VzZV09Jy92YXIvbG9nL2NsaWNraG91c2Utc2VydmVyJwpEQl9MQUJFTFtpbmZsdXhkYl09J0luZmx1eERCJzsgREJfU0VSVklDRVtpbmZsdXhkYl09J2luZmx1eGRiJzsgREJfQ09NTUFORFtpbmZsdXhkYl09J2luZmx1eCc7IERCX1BPUlRTW2luZmx1eGRiXT0nODA4Nic7IERCX0NPTkZJR1tpbmZsdXhkYl09Jy9ldGMvaW5mbHV4ZGInOyBEQl9EQVRBW2luZmx1eGRiXT0nL3Zhci9saWIvaW5mbHV4ZGInOyBEQl9MT0dTW2luZmx1eGRiXT0nL3Zhci9sb2cvaW5mbHV4ZGInCkRCX0xBQkVMW2Nhc3NhbmRyYV09J0Nhc3NhbmRyYSc7IERCX1NFUlZJQ0VbY2Fzc2FuZHJhXT0nY2Fzc2FuZHJhJzsgREJfQ09NTUFORFtjYXNzYW5kcmFdPSdjcWxzaCc7IERCX1BPUlRTW2Nhc3NhbmRyYV09JzkwNDInOyBEQl9DT05GSUdbY2Fzc2FuZHJhXT0nL2V0Yy9jYXNzYW5kcmEnOyBEQl9EQVRBW2Nhc3NhbmRyYV09Jy92YXIvbGliL2Nhc3NhbmRyYSc7IERCX0xPR1NbY2Fzc2FuZHJhXT0nL3Zhci9sb2cvY2Fzc2FuZHJhJwpEQl9MQUJFTFtyYWJiaXRtcV09J1JhYmJpdE1RJzsgREJfU0VSVklDRVtyYWJiaXRtcV09J3JhYmJpdG1xLXNlcnZlcic7IERCX0NPTU1BTkRbcmFiYml0bXFdPSdyYWJiaXRtcWN0bCc7IERCX1BPUlRTW3JhYmJpdG1xXT0nNTY3MiAxNTY3Mic7IERCX0NPTkZJR1tyYWJiaXRtcV09Jy9ldGMvcmFiYml0bXEnOyBEQl9EQVRBW3JhYmJpdG1xXT0nL3Zhci9saWIvcmFiYml0bXEnOyBEQl9MT0dTW3JhYmJpdG1xXT0nL3Zhci9sb2cvcmFiYml0bXEnCgpERVRFQ1RFRF9DT1VOVD0wCkxJU1RFTklOR19QT1JUX0NPVU5UPTAKRkFJTEVEX1NFUlZJQ0VfQ09VTlQ9MApMT0dfV0FSTklOR19DT1VOVD0wClNUT1JBR0VfV0FSTklOR19DT1VOVD0wClNUT1JBR0VfQ1JJVElDQUxfQ09VTlQ9MApCQUNLVVBfU1RBVFVTPSdOT1QgRk9VTkQnCk5FV0VTVF9CQUNLVVA9J04vQScKCnNlcnZpY2VfdW5pdF9leGlzdHMoKSB7CiAgICBsb2NhbCBzZXJ2aWNlPSIkMSIKICAgIHN5c3RlbWN0bCBsaXN0LXVuaXQtZmlsZXMgLS10eXBlPXNlcnZpY2UgLS1uby1sZWdlbmQgMj4vZGV2L251bGwgfCBhd2sgJ3twcmludCAkMX0nIHwgZ3JlcCAtRnhxICIke3NlcnZpY2V9LnNlcnZpY2UiCn0KCnBhY2thZ2VfaW5zdGFsbGVkX21hdGNoaW5nKCkgewogICAgbG9jYWwgcGF0dGVybj0iJDEiCiAgICBjb21tYW5kX2V4aXN0cyBkcGtnLXF1ZXJ5IHx8IHJldHVybiAxCiAgICBkcGtnLXF1ZXJ5IC1XIC1mPScke1BhY2thZ2V9XG4nIDI+L2Rldi9udWxsIHwgZ3JlcCAtRWlxICIkcGF0dGVybiIKfQoKZGV0ZWN0X2RhdGFiYXNlKCkgewogICAgbG9jYWwga2V5PSIkMSIgc2VydmljZT0iJHtEQl9TRVJWSUNFWyQxXX0iIGNtZD0iJHtEQl9DT01NQU5EWyQxXX0iIGNvbmZpZz0iJHtEQl9DT05GSUdbJDFdfSIgZGF0YT0iJHtEQl9EQVRBWyQxXX0iIGRldGVjdGVkPTAKICAgIHNlcnZpY2VfdW5pdF9leGlzdHMgIiRzZXJ2aWNlIiAmJiBkZXRlY3RlZD0xCiAgICBjb21tYW5kX2V4aXN0cyAiJGNtZCIgJiYgZGV0ZWN0ZWQ9MQogICAgeyBbIC1lICIkY29uZmlnIiBdIHx8IFsgLWQgIiRkYXRhIiBdOyB9ICYmIGRldGVjdGVkPTEKICAgIGNhc2UgIiRrZXkiIGluCiAgICAgICAgcG9zdGdyZXNxbCkgcGFja2FnZV9pbnN0YWxsZWRfbWF0Y2hpbmcgJ15wb3N0Z3Jlc3FsKFswLTkuLV18JCl8XnBvc3RncmVzcWwtJyAmJiBkZXRlY3RlZD0xIDs7CiAgICAgICAgbXlzcWwpIHBhY2thZ2VfaW5zdGFsbGVkX21hdGNoaW5nICdebXlzcWwtc2VydmVyfF5teXNxbC1jbGllbnQnICYmIGRldGVjdGVkPTEgOzsKICAgICAgICBtYXJpYWRiKSBwYWNrYWdlX2luc3RhbGxlZF9tYXRjaGluZyAnXm1hcmlhZGItc2VydmVyfF5tYXJpYWRiLWNsaWVudCcgJiYgZGV0ZWN0ZWQ9MSA7OwogICAgICAgIG1vbmdvZGIpIHBhY2thZ2VfaW5zdGFsbGVkX21hdGNoaW5nICdebW9uZ29kYnxebW9uZ29kYi1vcmcnICYmIGRldGVjdGVkPTEgOzsKICAgICAgICByZWRpcykgcGFja2FnZV9pbnN0YWxsZWRfbWF0Y2hpbmcgJ15yZWRpcy1zZXJ2ZXJ8XnJlZGlzLXRvb2xzJyAmJiBkZXRlY3RlZD0xIDs7CiAgICAgICAgZWxhc3RpY3NlYXJjaCkgcGFja2FnZV9pbnN0YWxsZWRfbWF0Y2hpbmcgJ15lbGFzdGljc2VhcmNoJCcgJiYgZGV0ZWN0ZWQ9MSA7OwogICAgICAgIG9wZW5zZWFyY2gpIHBhY2thZ2VfaW5zdGFsbGVkX21hdGNoaW5nICdeb3BlbnNlYXJjaCcgJiYgZGV0ZWN0ZWQ9MSA7OwogICAgICAgIGNsaWNraG91c2UpIHBhY2thZ2VfaW5zdGFsbGVkX21hdGNoaW5nICdeY2xpY2tob3VzZS0nICYmIGRldGVjdGVkPTEgOzsKICAgICAgICBpbmZsdXhkYikgcGFja2FnZV9pbnN0YWxsZWRfbWF0Y2hpbmcgJ15pbmZsdXhkYicgJiYgZGV0ZWN0ZWQ9MSA7OwogICAgICAgIGNhc3NhbmRyYSkgcGFja2FnZV9pbnN0YWxsZWRfbWF0Y2hpbmcgJ15jYXNzYW5kcmEkJyAmJiBkZXRlY3RlZD0xIDs7CiAgICAgICAgcmFiYml0bXEpIHBhY2thZ2VfaW5zdGFsbGVkX21hdGNoaW5nICdecmFiYml0bXEtc2VydmVyJCcgJiYgZGV0ZWN0ZWQ9MSA7OwogICAgZXNhYwogICAgREJfREVURUNURURbJGtleV09IiRkZXRlY3RlZCIKfQoKZ2V0X3NlcnZpY2Vfc3RhdGUoKSB7CiAgICBsb2NhbCBzZXJ2aWNlPSIkMSIgc3RhdGUKICAgIHNlcnZpY2VfdW5pdF9leGlzdHMgIiRzZXJ2aWNlIiB8fCB7IHByaW50ZiAnTk8gVU5JVCc7IHJldHVybjsgfQogICAgc3RhdGU9JChzeXN0ZW1jdGwgaXMtYWN0aXZlICIkc2VydmljZSIgMj4vZGV2L251bGwgfHwgdHJ1ZSkKICAgIGNhc2UgIiRzdGF0ZSIgaW4KICAgICAgICBhY3RpdmUpIHByaW50ZiAnUlVOTklORycgOzsKICAgICAgICBpbmFjdGl2ZSkgcHJpbnRmICdTVE9QUEVEJyA7OwogICAgICAgIGZhaWxlZCkgcHJpbnRmICdGQUlMRUQnIDs7CiAgICAgICAgYWN0aXZhdGluZykgcHJpbnRmICdTVEFSVElORycgOzsKICAgICAgICBkZWFjdGl2YXRpbmcpIHByaW50ZiAnU1RPUFBJTkcnIDs7CiAgICAgICAgKikgcHJpbnRmICclcycgIiR7c3RhdGVeXn0iIDs7CiAgICBlc2FjCn0KCmdldF92ZXJzaW9uKCkgewogICAgbG9jYWwga2V5PSIkMSIgb3V0cHV0PScnCiAgICBjYXNlICIka2V5IiBpbgogICAgICAgIHBvc3RncmVzcWwpIGNvbW1hbmRfZXhpc3RzIHBzcWwgJiYgb3V0cHV0PSQocHNxbCAtLXZlcnNpb24gMj4mMSB8IGhlYWQgLW4gMSkgOzsKICAgICAgICBteXNxbCkgY29tbWFuZF9leGlzdHMgbXlzcWwgJiYgb3V0cHV0PSQobXlzcWwgLS12ZXJzaW9uIDI+JjEgfCBoZWFkIC1uIDEpIDs7CiAgICAgICAgbWFyaWFkYikgY29tbWFuZF9leGlzdHMgbWFyaWFkYiAmJiBvdXRwdXQ9JChtYXJpYWRiIC0tdmVyc2lvbiAyPiYxIHwgaGVhZCAtbiAxKSA7OwogICAgICAgIG1vbmdvZGIpIGlmIGNvbW1hbmRfZXhpc3RzIG1vbmdvc2g7IHRoZW4gb3V0cHV0PSQobW9uZ29zaCAtLXZlcnNpb24gMj4mMSB8IGhlYWQgLW4gMSk7IGVsaWYgY29tbWFuZF9leGlzdHMgbW9uZ29kOyB0aGVuIG91dHB1dD0kKG1vbmdvZCAtLXZlcnNpb24gMj4mMSB8IGhlYWQgLW4gMSk7IGZpIDs7CiAgICAgICAgcmVkaXMpIGNvbW1hbmRfZXhpc3RzIHJlZGlzLXNlcnZlciAmJiBvdXRwdXQ9JChyZWRpcy1zZXJ2ZXIgLS12ZXJzaW9uIDI+JjEgfCBoZWFkIC1uIDEpIDs7CiAgICAgICAgZWxhc3RpY3NlYXJjaCkgaWYgY29tbWFuZF9leGlzdHMgZWxhc3RpY3NlYXJjaDsgdGhlbiBvdXRwdXQ9JChlbGFzdGljc2VhcmNoIC0tdmVyc2lvbiAyPiYxIHwgaGVhZCAtbiAxKTsgZWxpZiBbIC14IC91c3Ivc2hhcmUvZWxhc3RpY3NlYXJjaC9iaW4vZWxhc3RpY3NlYXJjaCBdOyB0aGVuIG91dHB1dD0kKC91c3Ivc2hhcmUvZWxhc3RpY3NlYXJjaC9iaW4vZWxhc3RpY3NlYXJjaCAtLXZlcnNpb24gMj4mMSB8IGhlYWQgLW4gMSk7IGZpIDs7CiAgICAgICAgb3BlbnNlYXJjaCkgaWYgY29tbWFuZF9leGlzdHMgb3BlbnNlYXJjaDsgdGhlbiBvdXRwdXQ9JChvcGVuc2VhcmNoIC0tdmVyc2lvbiAyPiYxIHwgaGVhZCAtbiAxKTsgZWxpZiBbIC14IC91c3Ivc2hhcmUvb3BlbnNlYXJjaC9iaW4vb3BlbnNlYXJjaCBdOyB0aGVuIG91dHB1dD0kKC91c3Ivc2hhcmUvb3BlbnNlYXJjaC9iaW4vb3BlbnNlYXJjaCAtLXZlcnNpb24gMj4mMSB8IGhlYWQgLW4gMSk7IGZpIDs7CiAgICAgICAgY2xpY2tob3VzZSkgY29tbWFuZF9leGlzdHMgY2xpY2tob3VzZS1jbGllbnQgJiYgb3V0cHV0PSQoY2xpY2tob3VzZS1jbGllbnQgLS12ZXJzaW9uIDI+JjEgfCBoZWFkIC1uIDEpIDs7CiAgICAgICAgaW5mbHV4ZGIpIGNvbW1hbmRfZXhpc3RzIGluZmx1eCAmJiBvdXRwdXQ9JChpbmZsdXggdmVyc2lvbiAyPiYxIHwgaGVhZCAtbiAxKSA7OwogICAgICAgIGNhc3NhbmRyYSkgY29tbWFuZF9leGlzdHMgY2Fzc2FuZHJhICYmIG91dHB1dD0kKGNhc3NhbmRyYSAtdiAyPiYxIHwgaGVhZCAtbiAxKSA7OwogICAgICAgIHJhYmJpdG1xKSBjb21tYW5kX2V4aXN0cyByYWJiaXRtcWN0bCAmJiBvdXRwdXQ9JChyYWJiaXRtcWN0bCB2ZXJzaW9uIDI+L2Rldi9udWxsIHwgaGVhZCAtbiAxKSA7OwogICAgZXNhYwogICAgWyAtbiAiJG91dHB1dCIgXSAmJiBwcmludGYgJyVzJyAiJG91dHB1dCIgfHwgcHJpbnRmICdVTktOT1dOJwp9CgpwcmludF9tYWluX2hlYWRlcgoKcHJpbnRfc2VjdGlvbiAnMTEuMSBEQVRBQkFTRSBESVNDT1ZFUlknCmZvciBrZXkgaW4gIiR7REFUQUJBU0VfS0VZU1tAXX0iOyBkbwogICAgZGV0ZWN0X2RhdGFiYXNlICIka2V5IgogICAgaWYgWyAiJHtEQl9ERVRFQ1RFRFska2V5XX0iIC1lcSAxIF07IHRoZW4KICAgICAgICBERVRFQ1RFRF9DT1VOVD0kKChERVRFQ1RFRF9DT1VOVCArIDEpKQogICAgICAgIERCX1ZFUlNJT05bJGtleV09IiQoZ2V0X3ZlcnNpb24gIiRrZXkiKSIKICAgICAgICBwcmludGYgJyAgJS0xOHMgJWJERVRFQ1RFRCViXG4nICIke0RCX0xBQkVMWyRrZXldfSIgIiRHUkVFTiIgIiROQyIKICAgICAgICBwcmludGYgJyAgICBWZXJzaW9uOiAlc1xuJyAiJHtEQl9WRVJTSU9OWyRrZXldfSIKICAgIGVsc2UKICAgICAgICBEQl9WRVJTSU9OWyRrZXldPSdOL0EnCiAgICAgICAgcHJpbnRmICcgICUtMThzIE5PVCBERVRFQ1RFRFxuJyAiJHtEQl9MQUJFTFska2V5XX0iCiAgICBmaQpkb25lClsgIiRERVRFQ1RFRF9DT1VOVCIgLWVxIDAgXSAmJiBwcmludF9pbmZvICdObyBzdXBwb3J0ZWQgZGF0YWJhc2Ugb3IgZGF0YSBzZXJ2aWNlIHdhcyBkZXRlY3RlZC4nIHx8IHByaW50X29rICIkREVURUNURURfQ09VTlQgc3VwcG9ydGVkIGRhdGFiYXNlIG9yIGRhdGEgc2VydmljZShzKSBkZXRlY3RlZC4iCnByaW50ZiAnXG4nCgpwcmludF9zZWN0aW9uICcxMS4yIFNFUlZJQ0UgU1RBVFVTJwpmb3Iga2V5IGluICIke0RBVEFCQVNFX0tFWVNbQF19IjsgZG8KICAgIGlmIFsgIiR7REJfREVURUNURURbJGtleV19IiAtbmUgMSBdOyB0aGVuIERCX1NUQVRVU1ska2V5XT0nTk9UIElOU1RBTExFRCc7IGNvbnRpbnVlOyBmaQogICAgREJfU1RBVFVTWyRrZXldPSIkKGdldF9zZXJ2aWNlX3N0YXRlICIke0RCX1NFUlZJQ0VbJGtleV19IikiCiAgICBjYXNlICIke0RCX1NUQVRVU1ska2V5XX0iIGluCiAgICAgICAgUlVOTklORykgcHJpbnRmICcgICUtMThzICViUlVOTklORyViXG4nICIke0RCX0xBQkVMWyRrZXldfSIgIiRHUkVFTiIgIiROQyIgOzsKICAgICAgICBGQUlMRUQpIHByaW50ZiAnICAlLTE4cyAlYkZBSUxFRCViXG4nICIke0RCX0xBQkVMWyRrZXldfSIgIiRSRUQiICIkTkMiOyBGQUlMRURfU0VSVklDRV9DT1VOVD0kKChGQUlMRURfU0VSVklDRV9DT1VOVCArIDEpKSA7OwogICAgICAgIFNUT1BQRUR8J05PIFVOSVQnKSBwcmludGYgJyAgJS0xOHMgJWIlcyViXG4nICIke0RCX0xBQkVMWyRrZXldfSIgIiRZRUxMT1ciICIke0RCX1NUQVRVU1ska2V5XX0iICIkTkMiIDs7CiAgICAgICAgKikgcHJpbnRmICcgICUtMThzICVzXG4nICIke0RCX0xBQkVMWyRrZXldfSIgIiR7REJfU1RBVFVTWyRrZXldfSIgOzsKICAgIGVzYWMKZG9uZQpbICIkRkFJTEVEX1NFUlZJQ0VfQ09VTlQiIC1ndCAwIF0gJiYgcHJpbnRfY3JpdGljYWwgIiRGQUlMRURfU0VSVklDRV9DT1VOVCBkYXRhYmFzZSBzZXJ2aWNlKHMpIGFyZSBpbiBmYWlsZWQgc3RhdGUuIgpwcmludGYgJ1xuJwoKcHJpbnRfc2VjdGlvbiAnMTEuMyBMSVNURU5JTkcgUE9SVFMnCmlmICEgY29tbWFuZF9leGlzdHMgc3M7IHRoZW4KICAgIHByaW50X3dhcm5pbmcgJ1RoZSBzcyBjb21tYW5kIGlzIHVuYXZhaWxhYmxlLicKZWxzZQogICAgZm9yIGtleSBpbiAiJHtEQVRBQkFTRV9LRVlTW0BdfSI7IGRvCiAgICAgICAgWyAiJHtEQl9ERVRFQ1RFRFska2V5XX0iIC1lcSAxIF0gfHwgY29udGludWUKICAgICAgICBmb3IgcG9ydCBpbiAke0RCX1BPUlRTWyRrZXldfTsgZG8KICAgICAgICAgICAgcG9ydF9vdXRwdXQ9JChzcyAtbG50dXAgMj4vZGV2L251bGwgfCBhd2sgLXYgcG9ydD0iOiRwb3J0IiAnJDUgfiBwb3J0IiQiIHx8ICQ1IH4gcG9ydCJbXjAtOV0iJykKICAgICAgICAgICAgaWYgWyAtbiAiJHBvcnRfb3V0cHV0IiBdOyB0aGVuCiAgICAgICAgICAgICAgICBMSVNURU5JTkdfUE9SVF9DT1VOVD0kKChMSVNURU5JTkdfUE9SVF9DT1VOVCArIDEpKQogICAgICAgICAgICAgICAgcHJpbnRmICcgICViJXMlYiAoJXMpXG4nICIkR1JFRU4iICIkcG9ydCIgIiROQyIgIiR7REJfTEFCRUxbJGtleV19IgogICAgICAgICAgICAgICAgcHJpbnRmICclc1xuJyAiJHBvcnRfb3V0cHV0IiB8IHNlZCAncy9eLyAgICAvJwogICAgICAgICAgICBmaQogICAgICAgIGRvbmUKICAgIGRvbmUKICAgIFsgIiRMSVNURU5JTkdfUE9SVF9DT1VOVCIgLWVxIDAgXSAmJiBwcmludF9pbmZvICdObyBrbm93biBkYXRhYmFzZSBsaXN0ZW5pbmcgcG9ydHMgd2VyZSBkZXRlY3RlZC4nCmZpCnByaW50ZiAnXG4nCgpwcmludF9zZWN0aW9uICcxMS40IElOU1RBTExFRCBWRVJTSU9OUycKZm9yIGtleSBpbiAiJHtEQVRBQkFTRV9LRVlTW0BdfSI7IGRvIFsgIiR7REJfREVURUNURURbJGtleV19IiAtZXEgMSBdICYmIHByaW50ZiAnICAlLTE4cyAlc1xuJyAiJHtEQl9MQUJFTFska2V5XX0iICIke0RCX1ZFUlNJT05bJGtleV19IjsgZG9uZQpbICIkREVURUNURURfQ09VTlQiIC1lcSAwIF0gJiYgcHJpbnRfaW5mbyAnTm90aGluZyB0byByZXBvcnQuJwpwcmludGYgJ1xuJwoKcHJpbnRfc2VjdGlvbiAnMTEuNSBEQVRBQkFTRSBQUk9DRVNTRVMnClBST0NFU1NfUEFUVEVSTj0ncG9zdGdyZXN8cG9zdG1hc3RlcnxteXNxbGR8bWFyaWFkYmR8bW9uZ29kfHJlZGlzLXNlcnZlcnxlbGFzdGljc2VhcmNofG9wZW5zZWFyY2h8Y2xpY2tob3VzZS1zZXJ2ZXJ8aW5mbHV4ZHxjYXNzYW5kcmF8YmVhbS5zbXAnCnByb2Nlc3Nfb3V0cHV0PSQocHMgLWVvIHBpZCxwcGlkLHVzZXIsJWNwdSwlbWVtLGV0aW1lLGNvbW0sYXJncyAtLXNvcnQ9LSVjcHUgMj4vZGV2L251bGwgfCBncmVwIC1FaSAiJFBST0NFU1NfUEFUVEVSTiIgfCBncmVwIC1FdiAnZ3JlcCAtRXxkYXRhYmFzZV9hc3Nlc3NtZW50JyB8IGhlYWQgLW4gMjUgfHwgdHJ1ZSkKWyAtbiAiJHByb2Nlc3Nfb3V0cHV0IiBdICYmIHByaW50ZiAnJXNcbicgIiRwcm9jZXNzX291dHB1dCIgfHwgcHJpbnRfaW5mbyAnTm8gbWF0Y2hpbmcgZGF0YWJhc2UgcHJvY2Vzc2VzIHdlcmUgZm91bmQuJwpwcmludGYgJ1xuJwoKcHJpbnRfc2VjdGlvbiAnMTEuNiBDT05GSUdVUkFUSU9OIEZJTEVTJwpmb3Iga2V5IGluICIke0RBVEFCQVNFX0tFWVNbQF19IjsgZG8KICAgIFsgIiR7REJfREVURUNURURbJGtleV19IiAtZXEgMSBdIHx8IGNvbnRpbnVlCiAgICBjb25maWc9IiR7REJfQ09ORklHWyRrZXldfSIKICAgIFsgLWUgIiRjb25maWciIF0gJiYgcHJpbnRmICcgICUtMThzICVzXG4nICIke0RCX0xBQkVMWyRrZXldfSIgIiRjb25maWciIHx8IHByaW50ZiAnICAlLTE4cyBOT1QgRk9VTkQgQVQgREVGQVVMVCBQQVRIXG4nICIke0RCX0xBQkVMWyRrZXldfSIKZG9uZQpwcmludF9pbmZvICdDb25maWd1cmF0aW9uIGNvbnRlbnRzIGFyZSBub3QgZGlzcGxheWVkIHRvIGF2b2lkIGV4cG9zaW5nIGNyZWRlbnRpYWxzIG9yIHNlY3JldHMuJwpwcmludGYgJ1xuJwoKcHJpbnRfc2VjdGlvbiAnMTEuNyBTVE9SQUdFIEFTU0VTU01FTlQnCmZvciBrZXkgaW4gIiR7REFUQUJBU0VfS0VZU1tAXX0iOyBkbwogICAgWyAiJHtEQl9ERVRFQ1RFRFska2V5XX0iIC1lcSAxIF0gfHwgY29udGludWUKICAgIGRhdGFfZGlyPSIke0RCX0RBVEFbJGtleV19IjsgWyAtZCAiJGRhdGFfZGlyIiBdIHx8IGNvbnRpbnVlCiAgICBzaXplPSQoZHUgLXNoeCAiJGRhdGFfZGlyIiAyPi9kZXYvbnVsbCB8IGF3ayAne3ByaW50ICQxfScpOyBbIC1uICIkc2l6ZSIgXSB8fCBzaXplPSdQRVJNSVNTSU9OIERFTklFRCcKICAgIGZzX2xpbmU9JChkZiAtUFRoICIkZGF0YV9kaXIiIDI+L2Rldi9udWxsIHwgdGFpbCAtbiAxKQogICAgZmlsZXN5c3RlbT0kKHByaW50ZiAnJXNcbicgIiRmc19saW5lIiB8IGF3ayAne3ByaW50ICQxfScpOyBmc190eXBlPSQocHJpbnRmICclc1xuJyAiJGZzX2xpbmUiIHwgYXdrICd7cHJpbnQgJDJ9JykKICAgIGF2YWlsYWJsZT0kKHByaW50ZiAnJXNcbicgIiRmc19saW5lIiB8IGF3ayAne3ByaW50ICQ1fScpOyB1c2VfcGN0PSQocHJpbnRmICclc1xuJyAiJGZzX2xpbmUiIHwgYXdrICd7Z3N1YigiJSIsIiIsJDYpOyBwcmludCAkNn0nKQogICAgbW91bnRwb2ludD0kKHByaW50ZiAnJXNcbicgIiRmc19saW5lIiB8IGF3ayAne3ByaW50ICQ3fScpCiAgICBvd25lcj0kKHN0YXQgLWMgJyVVOiVHJyAiJGRhdGFfZGlyIiAyPi9kZXYvbnVsbCB8fCBwcmludGYgJ1VOS05PV04nKQogICAgcGVybWlzc2lvbnM9JChzdGF0IC1jICclQSAoJWEpJyAiJGRhdGFfZGlyIiAyPi9kZXYvbnVsbCB8fCBwcmludGYgJ1VOS05PV04nKQogICAgbW91bnRfb3B0aW9ucz0kKGZpbmRtbnQgLW5vIE9QVElPTlMgLVQgIiRkYXRhX2RpciIgMj4vZGV2L251bGwgfHwgcHJpbnRmICdVTktOT1dOJykKICAgIHByaW50ZiAnICAlc1xuJyAiJHtEQl9MQUJFTFska2V5XX0iCiAgICBwcmludGYgJyAgICBEYXRhIGRpcmVjdG9yeSA6ICVzXG4gICAgRGlyZWN0b3J5IHNpemUgOiAlc1xuICAgIE93bmVyICAgICAgICAgICA6ICVzXG4gICAgUGVybWlzc2lvbnMgICAgIDogJXNcbicgIiRkYXRhX2RpciIgIiRzaXplIiAiJG93bmVyIiAiJHBlcm1pc3Npb25zIgogICAgcHJpbnRmICcgICAgRmlsZXN5c3RlbSAgICAgIDogJXMgKCVzKVxuICAgIE1vdW50IHBvaW50ICAgICA6ICVzXG4gICAgQXZhaWxhYmxlICAgICAgIDogJXNcbiAgICBVc2VkICAgICAgICAgICAgOiAlcyUlXG4gICAgTW91bnQgb3B0aW9ucyAgIDogJXNcbicgIiR7ZmlsZXN5c3RlbTotVU5LTk9XTn0iICIke2ZzX3R5cGU6LVVOS05PV059IiAiJHttb3VudHBvaW50Oi1VTktOT1dOfSIgIiR7YXZhaWxhYmxlOi1VTktOT1dOfSIgIiR7dXNlX3BjdDotVU5LTk9XTn0iICIkbW91bnRfb3B0aW9ucyIKICAgIGlmIFtbICIke3VzZV9wY3Q6LX0iID1+IF5bMC05XSskIF1dOyB0aGVuCiAgICAgICAgaWYgWyAiJHVzZV9wY3QiIC1nZSAiJEZJTEVTWVNURU1fQ1JJVElDQUxfVEhSRVNIT0xEIiBdOyB0aGVuIHByaW50X2NyaXRpY2FsICIke0RCX0xBQkVMWyRrZXldfSBzdG9yYWdlIGlzICR7dXNlX3BjdH0lIHVzZWQuIjsgU1RPUkFHRV9DUklUSUNBTF9DT1VOVD0kKChTVE9SQUdFX0NSSVRJQ0FMX0NPVU5UICsgMSkpCiAgICAgICAgZWxpZiBbICIkdXNlX3BjdCIgLWdlICIkRklMRVNZU1RFTV9XQVJOSU5HX1RIUkVTSE9MRCIgXTsgdGhlbiBwcmludF93YXJuaW5nICIke0RCX0xBQkVMWyRrZXldfSBzdG9yYWdlIGlzICR7dXNlX3BjdH0lIHVzZWQuIjsgU1RPUkFHRV9XQVJOSU5HX0NPVU5UPSQoKFNUT1JBR0VfV0FSTklOR19DT1VOVCArIDEpKQogICAgICAgIGVsc2UgcHJpbnRfb2sgIiR7REJfTEFCRUxbJGtleV19IHN0b3JhZ2UgdXRpbGl6YXRpb24gaXMgd2l0aGluIHRocmVzaG9sZC4iOyBmaQogICAgZmkKICAgIGNhc2UgIiwkbW91bnRfb3B0aW9ucywiIGluICoscm8sKikgcHJpbnRfY3JpdGljYWwgIiRkYXRhX2RpciBpcyBtb3VudGVkIHJlYWQtb25seS4iOyBTVE9SQUdFX0NSSVRJQ0FMX0NPVU5UPSQoKFNUT1JBR0VfQ1JJVElDQUxfQ09VTlQgKyAxKSkgOzsgZXNhYwogICAgcHJpbnRmICdcbicKZG9uZQppZiBbIC1kIC92YXIvbGliL3Bvc3RncmVzcWwgXTsgdGhlbgogICAgd2hpbGUgSUZTPSByZWFkIC1yIHdhbF9kaXI7IGRvIFsgLW4gIiR3YWxfZGlyIiBdIHx8IGNvbnRpbnVlOyB3YWxfc2l6ZT0kKGR1IC1zaHggIiR3YWxfZGlyIiAyPi9kZXYvbnVsbCB8IGF3ayAne3ByaW50ICQxfScpOyBwcmludGYgJyAgUG9zdGdyZVNRTCBXQUw6ICVzICglcylcbicgIiR3YWxfZGlyIiAiJHt3YWxfc2l6ZTotVU5LTk9XTn0iOyBkb25lIDwgPChmaW5kIC92YXIvbGliL3Bvc3RncmVzcWwgLXR5cGUgZCAtbmFtZSBwZ193YWwgLXByaW50IDI+L2Rldi9udWxsIHwgaGVhZCAtbiAxMCkKZmkKaWYgWyAtZCAvdmFyL2xpYi9teXNxbCBdOyB0aGVuCiAgICBiaW5sb2dfc3VtbWFyeT0kKGZpbmQgL3Zhci9saWIvbXlzcWwgLW1heGRlcHRoIDEgLXR5cGUgZiBcKCAtbmFtZSAnbXlzcWwtYmluLionIC1vIC1uYW1lICdtYXJpYWRiLWJpbi4qJyAtbyAtbmFtZSAnYmlubG9nLionIFwpIC1wcmludGYgJyVzXG4nIDI+L2Rldi9udWxsIHwgYXdrICd7c3VtKz0kMTsgY291bnQrK30gRU5EIHtpZiAoY291bnQ+MCkgcHJpbnRmICIlZCBmaWxlKHMpLCAlLjJmIEdpQiIsIGNvdW50LCBzdW0vMTAyNC8xMDI0LzEwMjR9JykKICAgIFsgLW4gIiRiaW5sb2dfc3VtbWFyeSIgXSAmJiBwcmludGYgJyAgTXlTUUwvTWFyaWFEQiBiaW5sb2dzOiAlc1xuJyAiJGJpbmxvZ19zdW1tYXJ5IgpmaQpwcmludGYgJ1xuJwoKcHJpbnRfc2VjdGlvbiAnMTEuOCBMT0cgQVNTRVNTTUVOVCcKRVJST1JfUEFUVEVSTj0ncGFuaWN8ZmF0YWx8Y29ycnVwdHxjb3JydXB0aW9ufG91dCBvZiBtZW1vcnl8b29tfGtpbGxlZCBwcm9jZXNzfGRpc2sgZnVsbHxubyBzcGFjZSBsZWZ0fHJlYWQtb25seSBmaWxlIHN5c3RlbXx0b28gbWFueSBjb25uZWN0aW9uc3xyZXBsaWNhdGlvbi4qZXJyb3J8d2FsLiplcnJvcnxjaGVja3BvaW50LiplcnJvcnxjcmFzaCByZWNvdmVyeXxzdGFydHVwLipmYWlsfGF1dGhlbnRpY2F0aW9uLipmYWlsfGFjY2VzcyBkZW5pZWQnCmpvdXJuYWxfZXJyb3JzPSQoam91cm5hbGN0bCAtLXNpbmNlICIkTE9HX0xPT0tCQUNLIiAtLW5vLXBhZ2VyIDI+L2Rldi9udWxsIHwgZ3JlcCAtRWkgIiRFUlJPUl9QQVRURVJOIiB8IHRhaWwgLW4gMjAgfHwgdHJ1ZSkKaWYgWyAtbiAiJGpvdXJuYWxfZXJyb3JzIiBdOyB0aGVuIHByaW50X3dhcm5pbmcgJ1BvdGVudGlhbCBkYXRhYmFzZS1yZWxhdGVkIGlzc3VlcyBmb3VuZCBpbiB0aGUgc3lzdGVtIGpvdXJuYWw6JzsgcHJpbnRmICclc1xuJyAiJGpvdXJuYWxfZXJyb3JzIiB8IHNlZCAncy9eLyAgLyc7IExPR19XQVJOSU5HX0NPVU5UPSQoKExPR19XQVJOSU5HX0NPVU5UICsgMSkpOyBlbHNlIHByaW50X29rICdObyBtYXRjaGluZyBjcml0aWNhbCBwYXR0ZXJucyBmb3VuZCBpbiB0aGUgc3lzdGVtIGpvdXJuYWwuJzsgZmkKZm9yIGtleSBpbiAiJHtEQVRBQkFTRV9LRVlTW0BdfSI7IGRvCiAgICBbICIke0RCX0RFVEVDVEVEWyRrZXldfSIgLWVxIDEgXSB8fCBjb250aW51ZQogICAgbG9nX3BhdGg9IiR7REJfTE9HU1ska2V5XX0iOyBbIC1lICIkbG9nX3BhdGgiIF0gfHwgY29udGludWUKICAgIG5hdGl2ZV9lcnJvcnM9JChncmVwIC1SaGlFICIkRVJST1JfUEFUVEVSTiIgIiRsb2dfcGF0aCIgMj4vZGV2L251bGwgfCB0YWlsIC1uIDEwIHx8IHRydWUpCiAgICBpZiBbIC1uICIkbmF0aXZlX2Vycm9ycyIgXTsgdGhlbiBwcmludF93YXJuaW5nICIke0RCX0xBQkVMWyRrZXldfSBuYXRpdmUgbG9ncyBjb250YWluIG1hdGNoaW5nIGVycm9yIHBhdHRlcm5zOiI7IHByaW50ZiAnJXNcbicgIiRuYXRpdmVfZXJyb3JzIiB8IHNlZCAncy9eLyAgLyc7IExPR19XQVJOSU5HX0NPVU5UPSQoKExPR19XQVJOSU5HX0NPVU5UICsgMSkpOyBmaQpkb25lCnByaW50ZiAnXG4nCgpwcmludF9zZWN0aW9uICcxMS45IFBPU1RHUkVTUUwgQVNTRVNTTUVOVCcKaWYgWyAiJHtEQl9ERVRFQ1RFRFtwb3N0Z3Jlc3FsXX0iIC1lcSAxIF07IHRoZW4KICAgIGNvbW1hbmRfZXhpc3RzIHBnX2xzY2x1c3RlcnMgJiYgcGdfbHNjbHVzdGVycyAyPi9kZXYvbnVsbCB8fCBwcmludF9pbmZvICdwZ19sc2NsdXN0ZXJzIGlzIHVuYXZhaWxhYmxlLicKICAgIHByaW50ZiAnXG4nCiAgICBjb21tYW5kX2V4aXN0cyBwZ19pc3JlYWR5ICYmIHBnX2lzcmVhZHkgMj4mMSB8fCBwcmludF9pbmZvICdwZ19pc3JlYWR5IGlzIHVuYXZhaWxhYmxlLicKZWxzZSBwcmludF9pbmZvICdQb3N0Z3JlU1FMIGlzIG5vdCBkZXRlY3RlZC4nOyBmaQpwcmludGYgJ1xuJwoKcHJpbnRfc2VjdGlvbiAnMTEuMTAgTVlTUUwgLyBNQVJJQURCIEFTU0VTU01FTlQnCmlmIFsgIiR7REJfREVURUNURURbbXlzcWxdfSIgLWVxIDEgXSB8fCBbICIke0RCX0RFVEVDVEVEW21hcmlhZGJdfSIgLWVxIDEgXTsgdGhlbgogICAgY29tbWFuZF9leGlzdHMgbXlzcWxhZG1pbiAmJiBteXNxbGFkbWluIC0tY29ubmVjdC10aW1lb3V0PTIgcGluZyAyPiYxIHwgaGVhZCAtbiAzIHx8IHByaW50X2luZm8gJ215c3FsYWRtaW4gaXMgdW5hdmFpbGFibGUuJwogICAgc29ja2V0X2xpbmVzPSQoZ3JlcCAtUmhpRSAnXltbOnNwYWNlOl1dKnNvY2tldFtbOnNwYWNlOl1dKj0nIC9ldGMvbXlzcWwgMj4vZGV2L251bGwgfCBzZWQgJ3MvXltbOnNwYWNlOl1dKi8vJyB8IGhlYWQgLW4gNSB8fCB0cnVlKQogICAgWyAtbiAiJHNvY2tldF9saW5lcyIgXSAmJiB7IHByaW50ZiAnICBDb25maWd1cmVkIHNvY2tldCBlbnRyaWVzOlxuJzsgcHJpbnRmICclc1xuJyAiJHNvY2tldF9saW5lcyIgfCBzZWQgJ3MvXi8gICAgLyc7IH0KZWxzZSBwcmludF9pbmZvICdNeVNRTCBhbmQgTWFyaWFEQiBhcmUgbm90IGRldGVjdGVkLic7IGZpCnByaW50ZiAnXG4nCgpwcmludF9zZWN0aW9uICcxMS4xMSBSRURJUyBBU1NFU1NNRU5UJwppZiBbICIke0RCX0RFVEVDVEVEW3JlZGlzXX0iIC1lcSAxIF07IHRoZW4KICAgIGlmIGNvbW1hbmRfZXhpc3RzIHJlZGlzLWNsaTsgdGhlbgogICAgICAgIGlmIHJlZGlzLWNsaSAtaCAxMjcuMC4wLjEgLXAgNjM3OSAtLW5vLWF1dGgtd2FybmluZyBwaW5nIDI+L2Rldi9udWxsIHwgZ3JlcCAtRnhxIFBPTkc7IHRoZW4KICAgICAgICAgICAgcHJpbnRfb2sgJ1JlZGlzIHJlc3BvbmRlZCB0byB1bmF1dGhlbnRpY2F0ZWQgbG9jYWwgUElORy4nCiAgICAgICAgICAgIHJlZGlzX2luZm89JChyZWRpcy1jbGkgLWggMTI3LjAuMC4xIC1wIDYzNzkgLS1uby1hdXRoLXdhcm5pbmcgSU5GTyBtZW1vcnkgMj4vZGV2L251bGwgfCBncmVwIC1FICdeKHVzZWRfbWVtb3J5X2h1bWFufG1heG1lbW9yeV9odW1hbnxtYXhtZW1vcnlfcG9saWN5KTonIHwgdHIgLWQgJ1xyJyB8fCB0cnVlKQogICAgICAgICAgICBbIC1uICIkcmVkaXNfaW5mbyIgXSAmJiBwcmludGYgJyVzXG4nICIkcmVkaXNfaW5mbyIgfCBzZWQgJ3MvXi8gIC8nCiAgICAgICAgZWxzZSBwcmludF9pbmZvICdSZWRpcyBkaWQgbm90IGFsbG93IGFuIHVuYXV0aGVudGljYXRlZCBsb2NhbCBoZWFsdGggcXVlcnkuJzsgZmkKICAgIGVsc2UgcHJpbnRfaW5mbyAncmVkaXMtY2xpIGlzIHVuYXZhaWxhYmxlLic7IGZpCmVsc2UgcHJpbnRfaW5mbyAnUmVkaXMgaXMgbm90IGRldGVjdGVkLic7IGZpCnByaW50ZiAnXG4nCgpwcmludF9zZWN0aW9uICcxMS4xMiBSRVBMSUNBVElPTiAvIENMVVNURVIgSU5ESUNBVE9SUycKUkVQTElDQVRJT05fUEFUVEVSTj0ncHJpbWFyeV9jb25uaW5mb3xzdGFuZGJ5X21vZGV8c3luY2hyb25vdXNfc3RhbmRieV9uYW1lc3x3YWxfbGV2ZWx8aG90X3N0YW5kYnl8c2VydmVyX2lkfGxvZ19iaW58cmVsYXlfbG9nfHJlYWRfb25seXx3c3JlcF9vbnx3c3JlcF9jbHVzdGVyfGdhbGVyYXxzZW50aW5lbCBtb25pdG9yfGNsdXN0ZXItZW5hYmxlZHxyZXBsaWNhb2Z8cmVwbGljYVNldHxjbHVzdGVyLm5hbWV8ZGlzY292ZXJ5LnNlZWRfaG9zdHMnCnJlcGxpY2F0aW9uX291dHB1dD0kKGdyZXAgLVJuaUUgIiRSRVBMSUNBVElPTl9QQVRURVJOIiAvZXRjL3Bvc3RncmVzcWwgL2V0Yy9teXNxbCAvZXRjL3JlZGlzIC9ldGMvZWxhc3RpY3NlYXJjaCAvZXRjL29wZW5zZWFyY2ggL2V0Yy9tb25nb2QuY29uZiAyPi9kZXYvbnVsbCB8IGdyZXAgLUVpdiAncGFzc3dvcmR8c2VjcmV0fHRva2VufGtleWZpbGV8Y3JlZGVudGlhbCcgfCBoZWFkIC1uIDMwIHx8IHRydWUpCmlmIFsgLW4gIiRyZXBsaWNhdGlvbl9vdXRwdXQiIF07IHRoZW4gcHJpbnRfaW5mbyAnUmVwbGljYXRpb24gb3IgY2x1c3RlcmluZyBpbmRpY2F0b3JzIHdlcmUgZm91bmQ6JzsgcHJpbnRmICclc1xuJyAiJHJlcGxpY2F0aW9uX291dHB1dCIgfCBzZWQgJ3MvXi8gIC8nOyBlbHNlIHByaW50X2luZm8gJ05vIGNsZWFyIHJlcGxpY2F0aW9uIG9yIGNsdXN0ZXJpbmcgaW5kaWNhdG9ycyBmb3VuZCBpbiBkZWZhdWx0IGNvbmZpZ3VyYXRpb24gcGF0aHMuJzsgZmkKcHJpbnRmICdcbicKCnByaW50X3NlY3Rpb24gJzExLjEzIEJBQ0tVUCBBU1NFU1NNRU5UJwpjcm9uX2JhY2t1cF9vdXRwdXQ9JChncmVwIC1SbmlFICdwZ19kdW1wfHBnX2Jhc2ViYWNrdXB8bXlzcWxkdW1wfG1hcmlhZGItZHVtcHxtb25nb2R1bXB8cmVkaXMuKihyZGJ8YW9mKXxjbGlja2hvdXNlLipiYWNrdXB8aW5mbHV4LipiYWNrdXB8Y3Fsc2guKmNvcHl8YmFja3VwJyAvZXRjL2Nyb24uZCAvZXRjL2Nyb24uZGFpbHkgL2V0Yy9jcm9uLmhvdXJseSAvZXRjL2Nyb24ud2Vla2x5IC9ldGMvY3Jvbi5tb250aGx5IC92YXIvc3Bvb2wvY3Jvbi9jcm9udGFicyAyPi9kZXYvbnVsbCB8IGdyZXAgLUVpdiAncGFzc3dvcmR8c2VjcmV0fHRva2VufGNyZWRlbnRpYWwnIHwgaGVhZCAtbiAyMCB8fCB0cnVlKQp0aW1lcl9iYWNrdXBfb3V0cHV0PSQoc3lzdGVtY3RsIGxpc3QtdGltZXJzIC0tYWxsIC0tbm8tcGFnZXIgMj4vZGV2L251bGwgfCBncmVwIC1FaSAnYmFja3VwfGR1bXB8cGdiYWNrcmVzdHx3YWwtZ3xib3JnfHJlc3RpYycgfCBoZWFkIC1uIDIwIHx8IHRydWUpCmJhY2t1cF9maWxlcz0kKGZpbmQgL3Zhci9iYWNrdXBzIC9iYWNrdXAgL3Nydi9iYWNrdXAgL29wdC9iYWNrdXAgL3Jvb3QgL2hvbWUgLXhkZXYgLXR5cGUgZiBcKCAtbmFtZSAnKi5zcWwnIC1vIC1uYW1lICcqLnNxbC5neicgLW8gLW5hbWUgJyouZHVtcCcgLW8gLW5hbWUgJyouYmFja3VwJyAtbyAtbmFtZSAnKi5yZGInIC1vIC1uYW1lICcqLmFvZicgLW8gLW5hbWUgJyouYnNvbicgLW8gLW5hbWUgJyoueGJzdHJlYW0nIFwpIC1wcmludGYgJyVUQHwlVFktJVRtLSVUZCAlVEg6JVRNfCVzfCVwXG4nIDI+L2Rldi9udWxsIHwgc29ydCAtdCd8JyAtazEsMW5yIHwgaGVhZCAtbiAxMCB8fCB0cnVlKQppZiBbIC1uICIkY3Jvbl9iYWNrdXBfb3V0cHV0IiBdOyB0aGVuIEJBQ0tVUF9TVEFUVVM9J0ZPVU5EJzsgcHJpbnRmICcgIENyb24gaW5kaWNhdG9yczpcbic7IHByaW50ZiAnJXNcbicgIiRjcm9uX2JhY2t1cF9vdXRwdXQiIHwgc2VkICdzL14vICAgIC8nOyBmaQppZiBbIC1uICIkdGltZXJfYmFja3VwX291dHB1dCIgXTsgdGhlbiBCQUNLVVBfU1RBVFVTPSdGT1VORCc7IHByaW50ZiAnICBzeXN0ZW1kIHRpbWVyIGluZGljYXRvcnM6XG4nOyBwcmludGYgJyVzXG4nICIkdGltZXJfYmFja3VwX291dHB1dCIgfCBzZWQgJ3MvXi8gICAgLyc7IGZpCmlmIFsgLW4gIiRiYWNrdXBfZmlsZXMiIF07IHRoZW4KICAgIEJBQ0tVUF9TVEFUVVM9J0ZPVU5EJzsgcHJpbnRmICcgIEJhY2t1cCBmaWxlczpcbicKICAgIHdoaWxlIElGUz0nfCcgcmVhZCAtciBlcG9jaCB0aW1lc3RhbXAgYnl0ZXMgcGF0aDsgZG8gWyAtbiAiJGVwb2NoIiBdIHx8IGNvbnRpbnVlOyBodW1hbl9zaXplPSQobnVtZm10IC0tdG89aWVjIC0tc3VmZml4PUIgIiRieXRlcyIgMj4vZGV2L251bGwgfHwgcHJpbnRmICclcyBieXRlcycgIiRieXRlcyIpOyBwcmludGYgJyAgICAlcyB8ICU4cyB8ICVzXG4nICIkdGltZXN0YW1wIiAiJGh1bWFuX3NpemUiICIkcGF0aCI7IGRvbmUgPDw8ICIkYmFja3VwX2ZpbGVzIgogICAgbmV3ZXN0X2Vwb2NoPSQocHJpbnRmICclc1xuJyAiJGJhY2t1cF9maWxlcyIgfCBoZWFkIC1uIDEgfCBjdXQgLWQnfCcgLWYxIHwgY3V0IC1kJy4nIC1mMSk7IG5ld2VzdF90aW1lc3RhbXA9JChwcmludGYgJyVzXG4nICIkYmFja3VwX2ZpbGVzIiB8IGhlYWQgLW4gMSB8IGN1dCAtZCd8JyAtZjIpCiAgICBpZiBbWyAiJG5ld2VzdF9lcG9jaCIgPX4gXlswLTldKyQgXV07IHRoZW4gbm93X2Vwb2NoPSQoZGF0ZSArJXMpOyBhZ2VfZGF5cz0kKCgobm93X2Vwb2NoIC0gbmV3ZXN0X2Vwb2NoKSAvIDg2NDAwKSk7IE5FV0VTVF9CQUNLVVA9IiR7bmV3ZXN0X3RpbWVzdGFtcH0gKCR7YWdlX2RheXN9IGRheShzKSBvbGQpIjsgWyAiJGFnZV9kYXlzIiAtZ3QgIiRCQUNLVVBfV0FSTklOR19EQVlTIiBdICYmIHByaW50X3dhcm5pbmcgIk5ld2VzdCBkaXNjb3ZlcmVkIGJhY2t1cCBmaWxlIGlzIG9sZGVyIHRoYW4gJHtCQUNLVVBfV0FSTklOR19EQVlTfSBkYXlzLiIgfHwgcHJpbnRfb2sgJ0EgcmVjZW50IGJhY2t1cCBmaWxlIHdhcyBkaXNjb3ZlcmVkLic7IGZpCmZpClsgIiRCQUNLVVBfU1RBVFVTIiA9ICdOT1QgRk9VTkQnIF0gJiYgcHJpbnRfd2FybmluZyAnTm8gZGF0YWJhc2UgYmFja3VwIGluZGljYXRvcnMgd2VyZSBmb3VuZCBpbiB0aGUgY2hlY2tlZCBsb2NhdGlvbnMuJwpwcmludGYgJ1xuJwoKcHJpbnRfc2VjdGlvbiAnMTEuMTQgREFUQUJBU0UgSEVBTFRIIFNVTU1BUlknCmZvciBrZXkgaW4gIiR7REFUQUJBU0VfS0VZU1tAXX0iOyBkbwogICAgWyAiJHtEQl9ERVRFQ1RFRFska2V5XX0iIC1lcSAxIF0gJiYgcHJpbnRmICcgICUtMjRzICVzXG4nICIke0RCX0xBQkVMWyRrZXldfSIgIiR7REJfU1RBVFVTWyRrZXldfSIgfHwgcHJpbnRmICcgICUtMjRzIE5PVCBJTlNUQUxMRURcbicgIiR7REJfTEFCRUxbJGtleV19Igpkb25lCmlmIFsgIiRTVE9SQUdFX0NSSVRJQ0FMX0NPVU5UIiAtZ3QgMCBdOyB0aGVuIFNUT1JBR0VfU1VNTUFSWT0nQ1JJVElDQUwnOyBlbGlmIFsgIiRTVE9SQUdFX1dBUk5JTkdfQ09VTlQiIC1ndCAwIF07IHRoZW4gU1RPUkFHRV9TVU1NQVJZPSdXQVJOSU5HJzsgZWxzZSBTVE9SQUdFX1NVTU1BUlk9J09LJzsgZmkKWyAiJEZBSUxFRF9TRVJWSUNFX0NPVU5UIiAtZ3QgMCBdICYmIFNFUlZJQ0VfU1VNTUFSWT0nQ1JJVElDQUwnIHx8IFNFUlZJQ0VfU1VNTUFSWT0nT0snClsgIiRMT0dfV0FSTklOR19DT1VOVCIgLWd0IDAgXSAmJiBMT0dfU1VNTUFSWT0nV0FSTklORycgfHwgTE9HX1NVTU1BUlk9J09LJwpwcmludGYgJ1xuICAlLTI0cyAlc1xuJyAnRGV0ZWN0ZWQgU2VydmljZXMnICIkREVURUNURURfQ09VTlQiCnByaW50ZiAnICAlLTI0cyAlc1xuJyAnTGlzdGVuaW5nIFBvcnRzJyAiJExJU1RFTklOR19QT1JUX0NPVU5UIgpwcmludGYgJyAgJS0yNHMgJXNcbicgJ1NlcnZpY2UgSGVhbHRoJyAiJFNFUlZJQ0VfU1VNTUFSWSIKcHJpbnRmICcgICUtMjRzICVzXG4nICdEYXRhYmFzZSBMb2dzJyAiJExPR19TVU1NQVJZIgpwcmludGYgJyAgJS0yNHMgJXNcbicgJ0RhdGFiYXNlIFN0b3JhZ2UnICIkU1RPUkFHRV9TVU1NQVJZIgpwcmludGYgJyAgJS0yNHMgJXNcbicgJ0JhY2t1cCBJbmRpY2F0b3JzJyAiJEJBQ0tVUF9TVEFUVVMiCnByaW50ZiAnICAlLTI0cyAlc1xuXG4nICdOZXdlc3QgQmFja3VwJyAiJE5FV0VTVF9CQUNLVVAiCmlmIFsgIiRGQUlMRURfU0VSVklDRV9DT1VOVCIgLWd0IDAgXSB8fCBbICIkU1RPUkFHRV9DUklUSUNBTF9DT1VOVCIgLWd0IDAgXTsgdGhlbiBwcmludF9jcml0aWNhbCAnRGF0YWJhc2UgYW5kIGRhdGEgc2VydmljZXMgcmVxdWlyZSBpbW1lZGlhdGUgYXR0ZW50aW9uLicKZWxpZiBbICIkTE9HX1dBUk5JTkdfQ09VTlQiIC1ndCAwIF0gfHwgWyAiJFNUT1JBR0VfV0FSTklOR19DT1VOVCIgLWd0IDAgXSB8fCBbICIkQkFDS1VQX1NUQVRVUyIgPSAnTk9UIEZPVU5EJyBdOyB0aGVuIHByaW50X3dhcm5pbmcgJ0RhdGFiYXNlIGFuZCBkYXRhIHNlcnZpY2VzIHJlcXVpcmUgcmV2aWV3LicKZWxzZSBwcmludF9vayAnTm8gY3JpdGljYWwgZGF0YWJhc2Ugb3IgZGF0YS1zZXJ2aWNlIGlzc3VlcyB3ZXJlIGRldGVjdGVkLic7IGZpCg==' | base64 -d | bash
#!/usr/bin/env bash
# ============================================================
# 11. DATABASE & DATA SERVICES ASSESSMENT
# Read-only diagnostics for Debian / Ubuntu systems
# No passwords are requested and no configuration is changed.
# ============================================================
set -u
set -o pipefail
if [ -t 1 ] && [ "${TERM:-dumb}" != "dumb" ]; then
GREEN='\033[0;32m'; RED='\033[0;31m'; YELLOW='\033[1;33m'
BLUE='\033[0;34m'; CYAN='\033[0;36m'; NC='\033[0m'
else
GREEN=''; RED=''; YELLOW=''; BLUE=''; CYAN=''; NC=''
fi
print_main_header() { printf '%b\n\n' "${BLUE}==================== 11. DATABASE & DATA SERVICES ====================${NC}"; }
print_section() { printf '%b\n' "${BLUE}--- $1 ---${NC}"; }
print_ok() { printf '%b\n' "${GREEN}[OK]${NC} $1"; }
print_info() { printf '%b\n' "${CYAN}[INFO]${NC} $1"; }
print_warning() { printf '%b\n' "${YELLOW}[WARNING]${NC} $1"; }
print_critical() { printf '%b\n' "${RED}[CRITICAL]${NC} $1"; }
command_exists() { command -v "$1" >/dev/null 2>&1; }
FILESYSTEM_WARNING_THRESHOLD=80
FILESYSTEM_CRITICAL_THRESHOLD=90
BACKUP_WARNING_DAYS=7
LOG_LOOKBACK='24 hours ago'
declare -A DB_LABEL DB_SERVICE DB_COMMAND DB_PORTS DB_CONFIG DB_DATA DB_LOGS
declare -A DB_DETECTED DB_STATUS DB_VERSION
DATABASE_KEYS=(postgresql mysql mariadb mongodb redis elasticsearch opensearch clickhouse influxdb cassandra rabbitmq)
DB_LABEL[postgresql]='PostgreSQL'; DB_SERVICE[postgresql]='postgresql'; DB_COMMAND[postgresql]='psql'; DB_PORTS[postgresql]='5432'; DB_CONFIG[postgresql]='/etc/postgresql'; DB_DATA[postgresql]='/var/lib/postgresql'; DB_LOGS[postgresql]='/var/log/postgresql'
DB_LABEL[mysql]='MySQL'; DB_SERVICE[mysql]='mysql'; DB_COMMAND[mysql]='mysql'; DB_PORTS[mysql]='3306'; DB_CONFIG[mysql]='/etc/mysql'; DB_DATA[mysql]='/var/lib/mysql'; DB_LOGS[mysql]='/var/log/mysql'
DB_LABEL[mariadb]='MariaDB'; DB_SERVICE[mariadb]='mariadb'; DB_COMMAND[mariadb]='mariadb'; DB_PORTS[mariadb]='3306'; DB_CONFIG[mariadb]='/etc/mysql'; DB_DATA[mariadb]='/var/lib/mysql'; DB_LOGS[mariadb]='/var/log/mysql'
DB_LABEL[mongodb]='MongoDB'; DB_SERVICE[mongodb]='mongod'; DB_COMMAND[mongodb]='mongosh'; DB_PORTS[mongodb]='27017'; DB_CONFIG[mongodb]='/etc/mongod.conf'; DB_DATA[mongodb]='/var/lib/mongodb'; DB_LOGS[mongodb]='/var/log/mongodb'
DB_LABEL[redis]='Redis'; DB_SERVICE[redis]='redis-server'; DB_COMMAND[redis]='redis-cli'; DB_PORTS[redis]='6379'; DB_CONFIG[redis]='/etc/redis'; DB_DATA[redis]='/var/lib/redis'; DB_LOGS[redis]='/var/log/redis'
DB_LABEL[elasticsearch]='Elasticsearch'; DB_SERVICE[elasticsearch]='elasticsearch'; DB_COMMAND[elasticsearch]='elasticsearch'; DB_PORTS[elasticsearch]='9200 9300'; DB_CONFIG[elasticsearch]='/etc/elasticsearch'; DB_DATA[elasticsearch]='/var/lib/elasticsearch'; DB_LOGS[elasticsearch]='/var/log/elasticsearch'
DB_LABEL[opensearch]='OpenSearch'; DB_SERVICE[opensearch]='opensearch'; DB_COMMAND[opensearch]='opensearch'; DB_PORTS[opensearch]='9200 9300'; DB_CONFIG[opensearch]='/etc/opensearch'; DB_DATA[opensearch]='/var/lib/opensearch'; DB_LOGS[opensearch]='/var/log/opensearch'
DB_LABEL[clickhouse]='ClickHouse'; DB_SERVICE[clickhouse]='clickhouse-server'; DB_COMMAND[clickhouse]='clickhouse-client'; DB_PORTS[clickhouse]='8123 9000'; DB_CONFIG[clickhouse]='/etc/clickhouse-server'; DB_DATA[clickhouse]='/var/lib/clickhouse'; DB_LOGS[clickhouse]='/var/log/clickhouse-server'
DB_LABEL[influxdb]='InfluxDB'; DB_SERVICE[influxdb]='influxdb'; DB_COMMAND[influxdb]='influx'; DB_PORTS[influxdb]='8086'; DB_CONFIG[influxdb]='/etc/influxdb'; DB_DATA[influxdb]='/var/lib/influxdb'; DB_LOGS[influxdb]='/var/log/influxdb'
DB_LABEL[cassandra]='Cassandra'; DB_SERVICE[cassandra]='cassandra'; DB_COMMAND[cassandra]='cqlsh'; DB_PORTS[cassandra]='9042'; DB_CONFIG[cassandra]='/etc/cassandra'; DB_DATA[cassandra]='/var/lib/cassandra'; DB_LOGS[cassandra]='/var/log/cassandra'
DB_LABEL[rabbitmq]='RabbitMQ'; DB_SERVICE[rabbitmq]='rabbitmq-server'; DB_COMMAND[rabbitmq]='rabbitmqctl'; DB_PORTS[rabbitmq]='5672 15672'; DB_CONFIG[rabbitmq]='/etc/rabbitmq'; DB_DATA[rabbitmq]='/var/lib/rabbitmq'; DB_LOGS[rabbitmq]='/var/log/rabbitmq'
DETECTED_COUNT=0
LISTENING_PORT_COUNT=0
FAILED_SERVICE_COUNT=0
LOG_WARNING_COUNT=0
STORAGE_WARNING_COUNT=0
STORAGE_CRITICAL_COUNT=0
BACKUP_STATUS='NOT FOUND'
NEWEST_BACKUP='N/A'
service_unit_exists() {
local service="$1"
systemctl list-unit-files --type=service --no-legend 2>/dev/null | awk '{print $1}' | grep -Fxq "${service}.service"
}
package_installed_matching() {
local pattern="$1"
command_exists dpkg-query || return 1
dpkg-query -W -f='${Package}\n' 2>/dev/null | grep -Eiq "$pattern"
}
detect_database() {
local key="$1" service="${DB_SERVICE[$1]}" cmd="${DB_COMMAND[$1]}" config="${DB_CONFIG[$1]}" data="${DB_DATA[$1]}" detected=0
service_unit_exists "$service" && detected=1
command_exists "$cmd" && detected=1
{ [ -e "$config" ] || [ -d "$data" ]; } && detected=1
case "$key" in
postgresql) package_installed_matching '^postgresql([0-9.-]|$)|^postgresql-' && detected=1 ;;
mysql) package_installed_matching '^mysql-server|^mysql-client' && detected=1 ;;
mariadb) package_installed_matching '^mariadb-server|^mariadb-client' && detected=1 ;;
mongodb) package_installed_matching '^mongodb|^mongodb-org' && detected=1 ;;
redis) package_installed_matching '^redis-server|^redis-tools' && detected=1 ;;
elasticsearch) package_installed_matching '^elasticsearch$' && detected=1 ;;
opensearch) package_installed_matching '^opensearch' && detected=1 ;;
clickhouse) package_installed_matching '^clickhouse-' && detected=1 ;;
influxdb) package_installed_matching '^influxdb' && detected=1 ;;
cassandra) package_installed_matching '^cassandra$' && detected=1 ;;
rabbitmq) package_installed_matching '^rabbitmq-server$' && detected=1 ;;
esac
DB_DETECTED[$key]="$detected"
}
get_service_state() {
local service="$1" state
service_unit_exists "$service" || { printf 'NO UNIT'; return; }
state=$(systemctl is-active "$service" 2>/dev/null || true)
case "$state" in
active) printf 'RUNNING' ;;
inactive) printf 'STOPPED' ;;
failed) printf 'FAILED' ;;
activating) printf 'STARTING' ;;
deactivating) printf 'STOPPING' ;;
*) printf '%s' "${state^^}" ;;
esac
}
get_version() {
local key="$1" output=''
case "$key" in
postgresql) command_exists psql && output=$(psql --version 2>&1 | head -n 1) ;;
mysql) command_exists mysql && output=$(mysql --version 2>&1 | head -n 1) ;;
mariadb) command_exists mariadb && output=$(mariadb --version 2>&1 | head -n 1) ;;
mongodb) if command_exists mongosh; then output=$(mongosh --version 2>&1 | head -n 1); elif command_exists mongod; then output=$(mongod --version 2>&1 | head -n 1); fi ;;
redis) command_exists redis-server && output=$(redis-server --version 2>&1 | head -n 1) ;;
elasticsearch) if command_exists elasticsearch; then output=$(elasticsearch --version 2>&1 | head -n 1); elif [ -x /usr/share/elasticsearch/bin/elasticsearch ]; then output=$(/usr/share/elasticsearch/bin/elasticsearch --version 2>&1 | head -n 1); fi ;;
opensearch) if command_exists opensearch; then output=$(opensearch --version 2>&1 | head -n 1); elif [ -x /usr/share/opensearch/bin/opensearch ]; then output=$(/usr/share/opensearch/bin/opensearch --version 2>&1 | head -n 1); fi ;;
clickhouse) command_exists clickhouse-client && output=$(clickhouse-client --version 2>&1 | head -n 1) ;;
influxdb) command_exists influx && output=$(influx version 2>&1 | head -n 1) ;;
cassandra) command_exists cassandra && output=$(cassandra -v 2>&1 | head -n 1) ;;
rabbitmq) command_exists rabbitmqctl && output=$(rabbitmqctl version 2>/dev/null | head -n 1) ;;
esac
[ -n "$output" ] && printf '%s' "$output" || printf 'UNKNOWN'
}
print_main_header
print_section '11.1 DATABASE DISCOVERY'
for key in "${DATABASE_KEYS[@]}"; do
detect_database "$key"
if [ "${DB_DETECTED[$key]}" -eq 1 ]; then
DETECTED_COUNT=$((DETECTED_COUNT + 1))
DB_VERSION[$key]="$(get_version "$key")"
printf ' %-18s %bDETECTED%b\n' "${DB_LABEL[$key]}" "$GREEN" "$NC"
printf ' Version: %s\n' "${DB_VERSION[$key]}"
else
DB_VERSION[$key]='N/A'
printf ' %-18s NOT DETECTED\n' "${DB_LABEL[$key]}"
fi
done
[ "$DETECTED_COUNT" -eq 0 ] && print_info 'No supported database or data service was detected.' || print_ok "$DETECTED_COUNT supported database or data service(s) detected."
printf '\n'
print_section '11.2 SERVICE STATUS'
for key in "${DATABASE_KEYS[@]}"; do
if [ "${DB_DETECTED[$key]}" -ne 1 ]; then DB_STATUS[$key]='NOT INSTALLED'; continue; fi
DB_STATUS[$key]="$(get_service_state "${DB_SERVICE[$key]}")"
case "${DB_STATUS[$key]}" in
RUNNING) printf ' %-18s %bRUNNING%b\n' "${DB_LABEL[$key]}" "$GREEN" "$NC" ;;
FAILED) printf ' %-18s %bFAILED%b\n' "${DB_LABEL[$key]}" "$RED" "$NC"; FAILED_SERVICE_COUNT=$((FAILED_SERVICE_COUNT + 1)) ;;
STOPPED|'NO UNIT') printf ' %-18s %b%s%b\n' "${DB_LABEL[$key]}" "$YELLOW" "${DB_STATUS[$key]}" "$NC" ;;
*) printf ' %-18s %s\n' "${DB_LABEL[$key]}" "${DB_STATUS[$key]}" ;;
esac
done
[ "$FAILED_SERVICE_COUNT" -gt 0 ] && print_critical "$FAILED_SERVICE_COUNT database service(s) are in failed state."
printf '\n'
print_section '11.3 LISTENING PORTS'
if ! command_exists ss; then
print_warning 'The ss command is unavailable.'
else
for key in "${DATABASE_KEYS[@]}"; do
[ "${DB_DETECTED[$key]}" -eq 1 ] || continue
for port in ${DB_PORTS[$key]}; do
port_output=$(ss -lntup 2>/dev/null | awk -v port=":$port" '$5 ~ port"$" || $5 ~ port"[^0-9]"')
if [ -n "$port_output" ]; then
LISTENING_PORT_COUNT=$((LISTENING_PORT_COUNT + 1))
printf ' %b%s%b (%s)\n' "$GREEN" "$port" "$NC" "${DB_LABEL[$key]}"
printf '%s\n' "$port_output" | sed 's/^/ /'
fi
done
done
[ "$LISTENING_PORT_COUNT" -eq 0 ] && print_info 'No known database listening ports were detected.'
fi
printf '\n'
print_section '11.4 INSTALLED VERSIONS'
for key in "${DATABASE_KEYS[@]}"; do [ "${DB_DETECTED[$key]}" -eq 1 ] && printf ' %-18s %s\n' "${DB_LABEL[$key]}" "${DB_VERSION[$key]}"; done
[ "$DETECTED_COUNT" -eq 0 ] && print_info 'Nothing to report.'
printf '\n'
print_section '11.5 DATABASE PROCESSES'
PROCESS_PATTERN='postgres|postmaster|mysqld|mariadbd|mongod|redis-server|elasticsearch|opensearch|clickhouse-server|influxd|cassandra|beam.smp'
process_output=$(ps -eo pid,ppid,user,%cpu,%mem,etime,comm,args --sort=-%cpu 2>/dev/null | grep -Ei "$PROCESS_PATTERN" | grep -Ev 'grep -E|database_assessment' | head -n 25 || true)
[ -n "$process_output" ] && printf '%s\n' "$process_output" || print_info 'No matching database processes were found.'
printf '\n'
print_section '11.6 CONFIGURATION FILES'
for key in "${DATABASE_KEYS[@]}"; do
[ "${DB_DETECTED[$key]}" -eq 1 ] || continue
config="${DB_CONFIG[$key]}"
[ -e "$config" ] && printf ' %-18s %s\n' "${DB_LABEL[$key]}" "$config" || printf ' %-18s NOT FOUND AT DEFAULT PATH\n' "${DB_LABEL[$key]}"
done
print_info 'Configuration contents are not displayed to avoid exposing credentials or secrets.'
printf '\n'
print_section '11.7 STORAGE ASSESSMENT'
for key in "${DATABASE_KEYS[@]}"; do
[ "${DB_DETECTED[$key]}" -eq 1 ] || continue
data_dir="${DB_DATA[$key]}"; [ -d "$data_dir" ] || continue
size=$(du -shx "$data_dir" 2>/dev/null | awk '{print $1}'); [ -n "$size" ] || size='PERMISSION DENIED'
fs_line=$(df -PTh "$data_dir" 2>/dev/null | tail -n 1)
filesystem=$(printf '%s\n' "$fs_line" | awk '{print $1}'); fs_type=$(printf '%s\n' "$fs_line" | awk '{print $2}')
available=$(printf '%s\n' "$fs_line" | awk '{print $5}'); use_pct=$(printf '%s\n' "$fs_line" | awk '{gsub("%","",$6); print $6}')
mountpoint=$(printf '%s\n' "$fs_line" | awk '{print $7}')
owner=$(stat -c '%U:%G' "$data_dir" 2>/dev/null || printf 'UNKNOWN')
permissions=$(stat -c '%A (%a)' "$data_dir" 2>/dev/null || printf 'UNKNOWN')
mount_options=$(findmnt -no OPTIONS -T "$data_dir" 2>/dev/null || printf 'UNKNOWN')
printf ' %s\n' "${DB_LABEL[$key]}"
printf ' Data directory : %s\n Directory size : %s\n Owner : %s\n Permissions : %s\n' "$data_dir" "$size" "$owner" "$permissions"
printf ' Filesystem : %s (%s)\n Mount point : %s\n Available : %s\n Used : %s%%\n Mount options : %s\n' "${filesystem:-UNKNOWN}" "${fs_type:-UNKNOWN}" "${mountpoint:-UNKNOWN}" "${available:-UNKNOWN}" "${use_pct:-UNKNOWN}" "$mount_options"
if [[ "${use_pct:-}" =~ ^[0-9]+$ ]]; then
if [ "$use_pct" -ge "$FILESYSTEM_CRITICAL_THRESHOLD" ]; then print_critical "${DB_LABEL[$key]} storage is ${use_pct}% used."; STORAGE_CRITICAL_COUNT=$((STORAGE_CRITICAL_COUNT + 1))
elif [ "$use_pct" -ge "$FILESYSTEM_WARNING_THRESHOLD" ]; then print_warning "${DB_LABEL[$key]} storage is ${use_pct}% used."; STORAGE_WARNING_COUNT=$((STORAGE_WARNING_COUNT + 1))
else print_ok "${DB_LABEL[$key]} storage utilization is within threshold."; fi
fi
case ",$mount_options," in *,ro,*) print_critical "$data_dir is mounted read-only."; STORAGE_CRITICAL_COUNT=$((STORAGE_CRITICAL_COUNT + 1)) ;; esac
printf '\n'
done
if [ -d /var/lib/postgresql ]; then
while IFS= read -r wal_dir; do [ -n "$wal_dir" ] || continue; wal_size=$(du -shx "$wal_dir" 2>/dev/null | awk '{print $1}'); printf ' PostgreSQL WAL: %s (%s)\n' "$wal_dir" "${wal_size:-UNKNOWN}"; done < <(find /var/lib/postgresql -type d -name pg_wal -print 2>/dev/null | head -n 10)
fi
if [ -d /var/lib/mysql ]; then
binlog_summary=$(find /var/lib/mysql -maxdepth 1 -type f \( -name 'mysql-bin.*' -o -name 'mariadb-bin.*' -o -name 'binlog.*' \) -printf '%s\n' 2>/dev/null | awk '{sum+=$1; count++} END {if (count>0) printf "%d file(s), %.2f GiB", count, sum/1024/1024/1024}')
[ -n "$binlog_summary" ] && printf ' MySQL/MariaDB binlogs: %s\n' "$binlog_summary"
fi
printf '\n'
print_section '11.8 LOG ASSESSMENT'
ERROR_PATTERN='panic|fatal|corrupt|corruption|out of memory|oom|killed process|disk full|no space left|read-only file system|too many connections|replication.*error|wal.*error|checkpoint.*error|crash recovery|startup.*fail|authentication.*fail|access denied'
journal_errors=$(journalctl --since "$LOG_LOOKBACK" --no-pager 2>/dev/null | grep -Ei "$ERROR_PATTERN" | tail -n 20 || true)
if [ -n "$journal_errors" ]; then print_warning 'Potential database-related issues found in the system journal:'; printf '%s\n' "$journal_errors" | sed 's/^/ /'; LOG_WARNING_COUNT=$((LOG_WARNING_COUNT + 1)); else print_ok 'No matching critical patterns found in the system journal.'; fi
for key in "${DATABASE_KEYS[@]}"; do
[ "${DB_DETECTED[$key]}" -eq 1 ] || continue
log_path="${DB_LOGS[$key]}"; [ -e "$log_path" ] || continue
native_errors=$(grep -RhiE "$ERROR_PATTERN" "$log_path" 2>/dev/null | tail -n 10 || true)
if [ -n "$native_errors" ]; then print_warning "${DB_LABEL[$key]} native logs contain matching error patterns:"; printf '%s\n' "$native_errors" | sed 's/^/ /'; LOG_WARNING_COUNT=$((LOG_WARNING_COUNT + 1)); fi
done
printf '\n'
print_section '11.9 POSTGRESQL ASSESSMENT'
if [ "${DB_DETECTED[postgresql]}" -eq 1 ]; then
command_exists pg_lsclusters && pg_lsclusters 2>/dev/null || print_info 'pg_lsclusters is unavailable.'
printf '\n'
command_exists pg_isready && pg_isready 2>&1 || print_info 'pg_isready is unavailable.'
else print_info 'PostgreSQL is not detected.'; fi
printf '\n'
print_section '11.10 MYSQL / MARIADB ASSESSMENT'
if [ "${DB_DETECTED[mysql]}" -eq 1 ] || [ "${DB_DETECTED[mariadb]}" -eq 1 ]; then
command_exists mysqladmin && mysqladmin --connect-timeout=2 ping 2>&1 | head -n 3 || print_info 'mysqladmin is unavailable.'
socket_lines=$(grep -RhiE '^[[:space:]]*socket[[:space:]]*=' /etc/mysql 2>/dev/null | sed 's/^[[:space:]]*//' | head -n 5 || true)
[ -n "$socket_lines" ] && { printf ' Configured socket entries:\n'; printf '%s\n' "$socket_lines" | sed 's/^/ /'; }
else print_info 'MySQL and MariaDB are not detected.'; fi
printf '\n'
print_section '11.11 REDIS ASSESSMENT'
if [ "${DB_DETECTED[redis]}" -eq 1 ]; then
if command_exists redis-cli; then
if redis-cli -h 127.0.0.1 -p 6379 --no-auth-warning ping 2>/dev/null | grep -Fxq PONG; then
print_ok 'Redis responded to unauthenticated local PING.'
redis_info=$(redis-cli -h 127.0.0.1 -p 6379 --no-auth-warning INFO memory 2>/dev/null | grep -E '^(used_memory_human|maxmemory_human|maxmemory_policy):' | tr -d '\r' || true)
[ -n "$redis_info" ] && printf '%s\n' "$redis_info" | sed 's/^/ /'
else print_info 'Redis did not allow an unauthenticated local health query.'; fi
else print_info 'redis-cli is unavailable.'; fi
else print_info 'Redis is not detected.'; fi
printf '\n'
print_section '11.12 REPLICATION / CLUSTER INDICATORS'
REPLICATION_PATTERN='primary_conninfo|standby_mode|synchronous_standby_names|wal_level|hot_standby|server_id|log_bin|relay_log|read_only|wsrep_on|wsrep_cluster|galera|sentinel monitor|cluster-enabled|replicaof|replicaSet|cluster.name|discovery.seed_hosts'
replication_output=$(grep -RniE "$REPLICATION_PATTERN" /etc/postgresql /etc/mysql /etc/redis /etc/elasticsearch /etc/opensearch /etc/mongod.conf 2>/dev/null | grep -Eiv 'password|secret|token|keyfile|credential' | head -n 30 || true)
if [ -n "$replication_output" ]; then print_info 'Replication or clustering indicators were found:'; printf '%s\n' "$replication_output" | sed 's/^/ /'; else print_info 'No clear replication or clustering indicators found in default configuration paths.'; fi
printf '\n'
print_section '11.13 BACKUP ASSESSMENT'
cron_backup_output=$(grep -RniE 'pg_dump|pg_basebackup|mysqldump|mariadb-dump|mongodump|redis.*(rdb|aof)|clickhouse.*backup|influx.*backup|cqlsh.*copy|backup' /etc/cron.d /etc/cron.daily /etc/cron.hourly /etc/cron.weekly /etc/cron.monthly /var/spool/cron/crontabs 2>/dev/null | grep -Eiv 'password|secret|token|credential' | head -n 20 || true)
timer_backup_output=$(systemctl list-timers --all --no-pager 2>/dev/null | grep -Ei 'backup|dump|pgbackrest|wal-g|borg|restic' | head -n 20 || true)
backup_files=$(find /var/backups /backup /srv/backup /opt/backup /root /home -xdev -type f \( -name '*.sql' -o -name '*.sql.gz' -o -name '*.dump' -o -name '*.backup' -o -name '*.rdb' -o -name '*.aof' -o -name '*.bson' -o -name '*.xbstream' \) -printf '%T@|%TY-%Tm-%Td %TH:%TM|%s|%p\n' 2>/dev/null | sort -t'|' -k1,1nr | head -n 10 || true)
if [ -n "$cron_backup_output" ]; then BACKUP_STATUS='FOUND'; printf ' Cron indicators:\n'; printf '%s\n' "$cron_backup_output" | sed 's/^/ /'; fi
if [ -n "$timer_backup_output" ]; then BACKUP_STATUS='FOUND'; printf ' systemd timer indicators:\n'; printf '%s\n' "$timer_backup_output" | sed 's/^/ /'; fi
if [ -n "$backup_files" ]; then
BACKUP_STATUS='FOUND'; printf ' Backup files:\n'
while IFS='|' read -r epoch timestamp bytes path; do [ -n "$epoch" ] || continue; human_size=$(numfmt --to=iec --suffix=B "$bytes" 2>/dev/null || printf '%s bytes' "$bytes"); printf ' %s | %8s | %s\n' "$timestamp" "$human_size" "$path"; done <<< "$backup_files"
newest_epoch=$(printf '%s\n' "$backup_files" | head -n 1 | cut -d'|' -f1 | cut -d'.' -f1); newest_timestamp=$(printf '%s\n' "$backup_files" | head -n 1 | cut -d'|' -f2)
if [[ "$newest_epoch" =~ ^[0-9]+$ ]]; then now_epoch=$(date +%s); age_days=$(((now_epoch - newest_epoch) / 86400)); NEWEST_BACKUP="${newest_timestamp} (${age_days} day(s) old)"; [ "$age_days" -gt "$BACKUP_WARNING_DAYS" ] && print_warning "Newest discovered backup file is older than ${BACKUP_WARNING_DAYS} days." || print_ok 'A recent backup file was discovered.'; fi
fi
[ "$BACKUP_STATUS" = 'NOT FOUND' ] && print_warning 'No database backup indicators were found in the checked locations.'
printf '\n'
print_section '11.14 DATABASE HEALTH SUMMARY'
for key in "${DATABASE_KEYS[@]}"; do
[ "${DB_DETECTED[$key]}" -eq 1 ] && printf ' %-24s %s\n' "${DB_LABEL[$key]}" "${DB_STATUS[$key]}" || printf ' %-24s NOT INSTALLED\n' "${DB_LABEL[$key]}"
done
if [ "$STORAGE_CRITICAL_COUNT" -gt 0 ]; then STORAGE_SUMMARY='CRITICAL'; elif [ "$STORAGE_WARNING_COUNT" -gt 0 ]; then STORAGE_SUMMARY='WARNING'; else STORAGE_SUMMARY='OK'; fi
[ "$FAILED_SERVICE_COUNT" -gt 0 ] && SERVICE_SUMMARY='CRITICAL' || SERVICE_SUMMARY='OK'
[ "$LOG_WARNING_COUNT" -gt 0 ] && LOG_SUMMARY='WARNING' || LOG_SUMMARY='OK'
printf '\n %-24s %s\n' 'Detected Services' "$DETECTED_COUNT"
printf ' %-24s %s\n' 'Listening Ports' "$LISTENING_PORT_COUNT"
printf ' %-24s %s\n' 'Service Health' "$SERVICE_SUMMARY"
printf ' %-24s %s\n' 'Database Logs' "$LOG_SUMMARY"
printf ' %-24s %s\n' 'Database Storage' "$STORAGE_SUMMARY"
printf ' %-24s %s\n' 'Backup Indicators' "$BACKUP_STATUS"
printf ' %-24s %s\n\n' 'Newest Backup' "$NEWEST_BACKUP"
if [ "$FAILED_SERVICE_COUNT" -gt 0 ] || [ "$STORAGE_CRITICAL_COUNT" -gt 0 ]; then print_critical 'Database and data services require immediate attention.'
elif [ "$LOG_WARNING_COUNT" -gt 0 ] || [ "$STORAGE_WARNING_COUNT" -gt 0 ] || [ "$BACKUP_STATUS" = 'NOT FOUND' ]; then print_warning 'Database and data services require review.'
else print_ok 'No critical database or data-service issues were detected.'; fi

