├── LICENSE.md ├── README.md ├── assets ├── README.md ├── banner.png ├── example-1.2.png ├── example-2.0.2.png └── example-2.0.jpg ├── pvekclean.sh └── version.txt /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Jordan Hillis - jordan@hillis.email - https://jordanhillis.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![PVEKCLEAN Logo](assets/banner.png) 2 | 3 | Easily remove old/unused PVE kernels on your Proxmox VE system 4 | 5 | [![Version](https://img.shields.io/badge/Version-v2.0.2-brightgreen)](https://github.com/jordanhillis/pvekclean) 6 | [![License: MIT](https://img.shields.io/badge/License-MIT-brightgreen.svg)](https://opensource.org/licenses/MIT) 7 | ![Updated](https://img.shields.io/github/last-commit/jordanhillis/pvekclean) 8 | ![Proxmox](https://img.shields.io/badge/-Proxmox-orange) 9 | ![Debian](https://img.shields.io/badge/-Debian-red) 10 | 11 | ### What is PVE Kernel Cleaner? 12 | 13 | PVE Kernel Cleaner is a program to compliment Proxmox Virtual Environment which is an open-source server virtualization environment. PVE Kernel Cleaner allows you to purge old/unused kernels filling the /boot directory. As new kernels are released the older ones have to be manually removed frequently to make room for newer ones. This can become quite tedious and require extensive time spent monitoring the system when new kernels are released and when older ones need to be cleared out to make room. With this issue existing, PVE Kernel Cleaner was created to solve it. 14 | 15 | ## Example Usage 16 | 17 | ![PVEKCLEAN Example](assets/example-2.0.2.png) 18 | 19 | ## Features 20 | 21 | * Removes old PVE kernels from your system 22 | * Ability to schedule PVE kernels to automatically be removed on a daily/weekly/monthly basis 23 | * Run a simple pvekclean command for ease of access 24 | * Checks health of boot disk based on space available 25 | * Debug mode for non-destructive testing 26 | * Update function to easily update the program to the latest version 27 | * Allows you to specify the minimum number of most recent PVE kernels to retain 28 | * Support for the latest Proxmox versions and PVE kernels 29 | 30 | ## Latest Version 31 | 32 | * v2.0.2 33 | 34 | ## Prerequisites 35 | 36 | Before using this program you will need to have the following packages installed. 37 | * cron 38 | * curl 39 | * git 40 | 41 | To install all required packages enter the following command. 42 | 43 | ##### Debian: 44 | 45 | ``` 46 | sudo apt-get install cron curl git 47 | ``` 48 | 49 | ## Installing 50 | 51 | You can install PVE Kernel Cleaner using either Git or Curl. Choose the method that suits you best: 52 | 53 | ### Installation via Git 54 | 55 | 1. Open your terminal. 56 | 57 | 2. Enter the following commands one by one to install PVE Kernel Cleaner: 58 | 59 | ```bash 60 | git clone https://github.com/jordanhillis/pvekclean.git 61 | cd pvekclean 62 | chmod +x pvekclean.sh 63 | ./pvekclean.sh 64 | ``` 65 | ### Installation via Curl 66 | 67 | 1. Open your terminal. 68 | 69 | 2. Use the following command to install PVE Kernel Cleaner: 70 | 71 | ```bash 72 | curl -o pvekclean.sh https://raw.githubusercontent.com/jordanhillis/pvekclean/master/pvekclean.sh 73 | chmod +x pvekclean.sh 74 | ./pvekclean.sh 75 | ``` 76 | 77 | ## Updating 78 | 79 | PVE Kernel Cleaner checks for updates automatically when you run it. If an update is available, you'll be notified within the program. Simply follow the on-screen instructions to install the update, and you're all set with the latest version! 80 | 81 | ## Usage 82 | 83 | Example of usage: 84 | ``` 85 | pvekclean [OPTION1] [OPTION2]... 86 | 87 | -k, --keep [number] Keep the specified number of most recent PVE kernels on the system 88 | Can be used with -f or --force for non-interactive removal 89 | -f, --force Force the removal of old PVE kernels without confirm prompts 90 | -rn, --remove-newer Remove kernels that are newer than the currently running kernel 91 | -s, --scheduler Have old PVE kernels removed on a scheduled basis 92 | -v, --version Shows current version of pvekclean 93 | -r, --remove Uninstall pvekclean from the system 94 | -i, --install Install pvekclean to the system 95 | -d, --dry-run Run the program in dry run mode for testing without making system changes 96 | 97 | ``` 98 | 99 | ## Usage Examples: 100 | Here are some common ways to use PVE Kernel Cleaner: 101 | 102 | * **Remove Old Kernels Non-Interactively:** 103 | ```bash 104 | pvekclean -f 105 | ``` 106 | This command removes old PVE kernels without requiring user confirmation. 107 | 108 | * **Set Number of Kernels to Keep:** 109 | ```bash 110 | pvekclean -k 3 111 | ``` 112 | This command specifies the number of most recent PVE kernels to keep on the system. 113 | 114 | * **Force Remove Old Kernels While Keeping a Certain Number:** 115 | ```bash 116 | pvekclean -f -k 3 117 | ``` 118 | This command forces the removal of old PVE kernels while retaining a specific number of the most recent ones. 119 | 120 | * **Remove Newer Kernels and Keep a Specific Number:** 121 | ```bash 122 | pvekclean -rn -k 2 123 | ``` 124 | This command removes newer PVE kernels and keeps a specified number of the most recent ones. 125 | 126 | * **Schedule Regular Kernel Removal:** 127 | ```bash 128 | pvekclean -s 129 | ``` 130 | This command sets up PVE Kernel Cleaner to remove old PVE kernels on a scheduled basis. You can configure the schedule according to your needs. 131 | 132 | * **Perform a Dry Run without Making Changes:** 133 | ```bash 134 | pvekclean -d 135 | ``` 136 | This command runs PVE Kernel Cleaner in dry run mode, simulating actions without actually removing any kernels or making changes to your system. It's useful for testing and understanding what the script would do. 137 | 138 | ## Developers 139 | 140 | * **Jordan Hillis** - *Lead Developer* 141 | 142 | ## License 143 | 144 | This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details 145 | 146 | ## Acknowledgments 147 | 148 | * This program is not an official program by Proxmox Server Solutions GmbH 149 | -------------------------------------------------------------------------------- /assets/README.md: -------------------------------------------------------------------------------- 1 | Asset files here only. Not intended for program use. 2 | -------------------------------------------------------------------------------- /assets/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jordanhillis/pvekclean/5739796801762520bcf4d66ad1b2b09638ad373b/assets/banner.png -------------------------------------------------------------------------------- /assets/example-1.2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jordanhillis/pvekclean/5739796801762520bcf4d66ad1b2b09638ad373b/assets/example-1.2.png -------------------------------------------------------------------------------- /assets/example-2.0.2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jordanhillis/pvekclean/5739796801762520bcf4d66ad1b2b09638ad373b/assets/example-2.0.2.png -------------------------------------------------------------------------------- /assets/example-2.0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jordanhillis/pvekclean/5739796801762520bcf4d66ad1b2b09638ad373b/assets/example-2.0.jpg -------------------------------------------------------------------------------- /pvekclean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | : ' 3 | ______________________________________________ 4 | 5 | PVE Kernel Cleaner 6 | By Jordan Hillis 7 | jordan@hillis.email 8 | https://jordanhillis.com 9 | ______________________________________________ 10 | 11 | MIT License 12 | 13 | Copyright (c) 2023 Jordan Hillis - jordan@hillis.email - https://jordanhillis.com 14 | 15 | Permission is hereby granted, free of charge, to any person obtaining a copy 16 | of this software and associated documentation files (the "Software"), to deal 17 | in the Software without restriction, including without limitation the rights 18 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 | copies of the Software, and to permit persons to whom the Software is 20 | furnished to do so, subject to the following conditions: 21 | The above copyright notice and this permission notice shall be included in all 22 | copies or substantial portions of the Software. 23 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29 | SOFTWARE. 30 | ______________________________________________ 31 | ' 32 | 33 | # Percentage of used space in the /boot which would consider it critically full 34 | boot_critical_percent="80" 35 | 36 | # To check for updates or not 37 | check_for_updates=true 38 | 39 | # Dry run mode is for testing without actually removing anything 40 | dry_run=false 41 | 42 | # Current kernel 43 | current_kernel=$(uname -r) 44 | 45 | # Name of the program 46 | program_name="pvekclean" 47 | 48 | # Version 49 | version="2.0.2" 50 | 51 | # Text Colors 52 | black="\e[38;2;0;0;0m" 53 | gray="\e[30m" 54 | red="\e[31m" 55 | green="\e[32m" 56 | yellow="\e[33m" 57 | blue="\e[34m" 58 | magenta="\e[35m" 59 | cyan="\e[36m" 60 | white="\e[37m" 61 | orange="\e[38;5;202m" 62 | 63 | # Background Colors 64 | bg_black="\e[40m" 65 | bg_red="\e[41m" 66 | bg_green="\e[42m" 67 | bg_yellow="\e[43m" 68 | bg_blue="\e[44m" 69 | bg_magenta="\e[45m" 70 | bg_cyan="\e[46m" 71 | bg_white="\e[47m" 72 | bg_orange="\e[48;5;202m" 73 | 74 | # Text Styles 75 | bold="\e[1m" 76 | 77 | # Reset formatting 78 | reset="\e[0m" 79 | 80 | # Force purging without dialog confirms 81 | force_purge=false 82 | 83 | # Allow removing kernels newer than current 84 | remove_newer=false 85 | 86 | # Check if script is ran as root, if not exit 87 | check_root() { 88 | if [[ $EUID -ne 0 ]]; then 89 | printf "${bold}[!] Error:${reset} this script must be ran as the root user.\n" 90 | exit 1 91 | fi 92 | } 93 | 94 | # Shown current version 95 | version() { 96 | printf $version"\n" 97 | exit 0 98 | } 99 | 100 | # Header for PVE Kernel Cleaner 101 | header_info() { 102 | echo -e " ${bg_black}${orange} ${reset} 103 | ${bg_black}${orange} █▀▀█ ▀█ █▀ █▀▀ █ █ █▀▀ █▀▀█ █▀▀▄ █▀▀ █ ${reset} 104 | ${bg_black}${orange} █ █ █▄█ █▀▀ █▀▄ █▀▀ █▄▄▀ █ █ █▀▀ █ ${reset} 105 | ${bg_black}${orange} █▀▀▀ ▀ ▀▀▀ ▀ ▀ ▀▀▀ ▀ ▀▀ ▀ ▀ ▀▀▀ ▀▀▀ ${reset} 106 | ${bg_black}${orange} ${reset} 107 | ${bg_black}${white} █▀▀ █ █▀▀ █▀▀█ █▀▀▄ █▀▀ █▀▀█ ${reset} 108 | ${bg_black}${white} █ █ █▀▀ █▄▄█ █ █ █▀▀ █▄▄▀ ${white}${bold}⎦˚◡˚⎣ v$version ${reset} 109 | ${bg_black}${white} ▀▀▀ ▀▀▀ ▀▀▀ ▀ ▀ ▀ ▀ ▀▀▀ ▀ ▀▀ ${reset} 110 | ${bg_orange}${black} ${bold}By Jordan Hillis [jordan@hillis.email] ${reset} 111 | ___________________________________________ 112 | " 113 | if [ "$dry_run" == "true" ]; then 114 | printf " ${bg_yellow}${black}${bold} DRY RUN MODE IS: ${red}ON ${reset}\n" 115 | printf "${bg_green}${bold}${black} This is what the script would do in regular mode ${reset}\n${bg_green}${bold}${black} (but without making actual changes) ${reset}\n\n" 116 | fi 117 | } 118 | 119 | # Function to get drive status based on usage percentage 120 | get_drive_status() { 121 | local usage=$1 122 | # Check if the input is a number 123 | if ! [[ $usage =~ ^[0-9]+$ ]]; then 124 | echo "${bold}N/A${reset}" 125 | else 126 | if (( usage <= 50 )); then 127 | echo "${bold}${green}Healthy${reset}" 128 | elif (( usage > 50 && usage <= 75 )); then 129 | echo "${bold}${orange}Moderate Capacity${reset}" 130 | else 131 | echo "${bold}${red}Critically Full${reset}" 132 | fi 133 | fi 134 | } 135 | 136 | # Show current system information 137 | kernel_info() { 138 | # Lastest kernel installed 139 | latest_kernel=$(dpkg --list | awk '/proxmox-kernel-.*-pve/{print $2}' | sed -n 's/proxmox-kernel-//p' | sort -V | tail -n 1 | tr -d '[:space:]') 140 | [ -z "$latest_kernel" ] && latest_kernel="N/A" 141 | # Show operating system used 142 | printf " ${bold}OS:${reset} $(cat /etc/os-release | grep "PRETTY_NAME" | sed 's/PRETTY_NAME=//g' | sed 's/["]//g' | awk '{print $0}')\n" 143 | # Get information about the /boot folder 144 | boot_info=($(echo $(df -Ph | grep /boot | tail -1) | sed 's/%//g')) 145 | # Show information about the /boot 146 | printf " ${bold}Boot Disk:${reset} ${boot_info[4]}%% full [${boot_info[2]}/${boot_info[1]} used, ${boot_info[3]} free] \n" 147 | # Show current kernel in use 148 | printf " ${bold}Current Kernel:${reset} $current_kernel\n" 149 | # Check if they are running a PVE kernel 150 | if [[ "$current_kernel" == *"pve"* ]]; then 151 | # Check if we are running the latest kernel, if not warn 152 | if [[ "$latest_kernel" != *"$current_kernel"* ]]; then 153 | printf " ${bold}Latest Kernel:${reset} ${latest_kernel}\n" 154 | fi 155 | # Warn them that they aren't on a PVE kernel 156 | else 157 | printf "___________________________________________\n\n" 158 | printf "${bold}[!]${reset} Warning, you're not running a PVE kernel\n" 159 | # Ask them if they want to continue 160 | printf "${bold}[*]${reset} Would you like to continue [y/N] " 161 | read -n 1 -r 162 | printf "\n" 163 | if [[ $REPLY =~ ^[Yy]$ ]]; then 164 | # Continue on if they wish 165 | printf "${bold}[-]${reset} Alright, we will continue on\n" 166 | else 167 | # Exit script 168 | printf "\nGood bye!\n" 169 | exit 0 170 | fi 171 | fi 172 | printf "___________________________________________\n\n" 173 | } 174 | 175 | # Usage information on how to use PVE Kernel Clean 176 | show_usage() { 177 | # Skip showing usage when force_purge is enabled 178 | if [ $force_purge == false ]; then 179 | printf "${bold}Usage:${reset} $(basename $0) [OPTION1] [OPTION2]...\n\n" 180 | printf " -k, --keep [number] Keep the specified number of most recent PVE kernels on the system\n" 181 | printf " Can be used with -f or --force for non-interactive removal\n" 182 | printf " -f, --force Force the removal of old PVE kernels without confirm prompts\n" 183 | printf " -rn, --remove-newer Remove kernels that are newer than the currently running kernel\n" 184 | printf " -s, --scheduler Have old PVE kernels removed on a scheduled basis\n" 185 | printf " -v, --version Shows current version of $program_name\n" 186 | printf " -r, --remove Uninstall $program_name from the system\n" 187 | printf " -i, --install Install $program_name to the system\n" 188 | printf " -d, --dry-run Run the program in dry run mode for testing without making system changes\n" 189 | printf "___________________________________________\n\n" 190 | fi 191 | } 192 | 193 | # Schedule PVE Kernel Cleaner at a time desired 194 | scheduler() { 195 | # Check if pvekclean is on the system, if not exit 196 | if [ ! -f /usr/local/sbin/$program_name ]; then 197 | printf "${bold}[!]${reset} Sorry $program_name is required to be installed on the system for this functionality.\n" 198 | exit 1 199 | fi 200 | # Check if cron is installed 201 | if ! [ -x "$(command -v crontab)" ]; then 202 | printf "${bold}[*]${reset} Error, cron does not appear to be installed.\n" 203 | printf " Please install cron with the command 'sudo apt-get install cron'\n\n" 204 | exit 1 205 | fi 206 | # Check if the cronjob exists on the system 207 | check_cron_exists=$(crontab -l | grep "$program_name") 208 | # Cronjob exists 209 | if [ -n "$check_cron_exists" ]; then 210 | # Get the current cronjob scheduling 211 | cron_current=$(crontab -l | grep "$program_name" | sed "s/[^a-zA-Z']/ /g" | sed -e "s/\b\(.\)/\u\1/g" | awk '{print $1;}') 212 | # Ask the user if they would like to remove the scheduling 213 | printf "${bold}[-]${reset} Would you like to remove the currently scheduled PVE Kernel Cleaner? (Current: $cron_current) [y/N] " 214 | read -n 1 -r 215 | if [[ $REPLY =~ ^[Yy]$ ]]; then 216 | # Remove the cronjob 217 | (crontab -l | grep -v "$program_name")| crontab - 218 | printf "\n[*] Successfully removed the ${cron_current,,} scheduled PVE Kernel Cleaner!\n" 219 | else 220 | # Keep it 221 | printf "\n\nAlright we will keep your current settings then.\n" 222 | fi 223 | # Cronjob does not exist 224 | else 225 | # Ask how often the would like to check for old PVE kernels 226 | printf "${bold}[-]${reset} How often would you like to check for old PVE kernels?\n 1) Daily\n 2) Weekly\n 3) Monthly\n\n - Enter a number option above? " 227 | read -r -p "" response 228 | case "$response" in 229 | 1) 230 | cron_time="daily" 231 | ;; 232 | 2) 233 | cron_time="weekly" 234 | ;; 235 | 3) 236 | cron_time="monthly" 237 | ;; 238 | *) 239 | printf "\nThat is not a valid option!\n" 240 | exit 1 241 | ;; 242 | esac 243 | # Ask if they want to set a specific number of kernels to keep 244 | printf "${bold}[-]${reset} Enter the number of latest kernels to keep (or press Enter to skip): " 245 | read number_of_kernels 246 | if [[ "$number_of_kernels" =~ ^[0-9]+$ ]]; then 247 | kernel_option=" -k $number_of_kernels" 248 | printf "${bold}[-]${reset} Okay, we will keep at least $number_of_kernels kernels on the system." 249 | else 250 | kernel_option="" 251 | fi 252 | # Add the cronjob 253 | (crontab -l ; echo "@$cron_time /usr/local/sbin/$program_name -f$kernel_option")| crontab - 254 | printf "\n[-] Scheduled $cron_time PVE Kernel Cleaner successfully!\n" 255 | fi 256 | exit 0 257 | } 258 | 259 | # Installs PVE Kernel Cleaner for easier access 260 | install_program() { 261 | force_pvekclean_update=false 262 | local tmp_file="/tmp/.pvekclean_install_lock" 263 | local install=false 264 | local ask_interval=3600 # 1 hour in seconds 265 | # If pvekclean exists on the system 266 | if [ -e /usr/local/sbin/$program_name ]; then 267 | # Get current version of pvekclean 268 | pvekclean_installed_version=$(/usr/local/sbin/$program_name -v | awk '{printf $0}') 269 | # If the version differs, update it to the latest from the script 270 | if [ $version != $pvekclean_installed_version ] && [ $force_purge == false ]; then 271 | printf "${bold}[!]${reset} A new version of PVE Kernel Cleaner has been detected (Installed: $pvekclean_installed_version | New: $version).\n" 272 | printf "${bold}[*]${reset} Installing update...\n" 273 | force_pvekclean_update=true 274 | fi 275 | fi 276 | # Check if the file doesn't exist or it's been over an hour since the last ask 277 | if [ ! -e "$tmp_file" ] || [ ! -f "$tmp_file" ] || [ $(( $(date +%s) - $(cat "$tmp_file") )) -gt $ask_interval ] || [ $force_pvekclean_update == true ] || [ -n "$force_pvekclean_install" ]; then 278 | # If pvekclean does not exist on the system or force_purge is enabled 279 | if [ ! -f /usr/local/sbin/$program_name ] || [ $force_pvekclean_update == true ] || [ -n "$force_pvekclean_install" ]; then 280 | # Ask user if we can install it to their system 281 | if [ $force_purge == true ]; then 282 | REPLY="n" 283 | else 284 | # Update the timestamp in the file to record the time of the last ask 285 | echo $(date +%s) > "$tmp_file" 286 | # Ask if we can install it 287 | printf "${bold}[-]${reset} Can we install PVE Kernel Cleaner to your /usr/local/sbin for easier access [y/N] " 288 | read -n 1 -r 289 | printf "\n" 290 | fi 291 | # User agrees to have it installed 292 | if [[ $REPLY =~ ^[Yy]$ ]]; then 293 | # Copy the script to /usr/local/sbin and set execution permissions 294 | cp $0 /usr/local/sbin/$program_name 295 | chmod +x /usr/local/sbin/$program_name 296 | # Tell user how to use it 297 | printf "${bold}[*]${reset} Installed PVE Kernel Cleaner to /usr/local/sbin/$program_name\n" 298 | printf "${bold}[*]${reset} Run the command \"$program_name\" to begin using this program.\n" 299 | printf "${bold}[-]${reset} Run the command \"$program_name -r\" to remove this program at any time.\n" 300 | exit 0 301 | fi 302 | fi 303 | fi 304 | if [ -n "$force_pvekclean_install" ]; then 305 | exit 0 306 | fi 307 | } 308 | 309 | # Uninstall pvekclean from the system 310 | uninstall_program() { 311 | # If pvekclean exists on the system 312 | if [ -e /usr/local/sbin/$program_name ]; then 313 | # Confirm that they wish to remove it 314 | printf "${bold}[-]${reset} Are you sure that you would like to remove $program_name? [y/N] " 315 | read -n 1 -r 316 | printf "\n" 317 | if [[ $REPLY =~ ^[Yy]$ ]]; then 318 | # Remove the program 319 | rm -f /usr/local/sbin/$program_name 320 | printf "${bold}[*]${reset} Successfully removed PVE Kernel Cleaner from the system!\n" 321 | printf "${bold}[-]${reset} Sorry to see you go :(\n" 322 | else 323 | printf "\nExiting...\nThat was a close one ⎦˚◡˚⎣\n" 324 | fi 325 | exit 0 326 | else 327 | # Tell the user that it is not installed 328 | printf "${bold}[!]${reset} This program is not installed on the system.\n" 329 | exit 1 330 | fi 331 | } 332 | 333 | # PVE Kernel Clean main function 334 | pve_kernel_clean() { 335 | # Find all the PVE kernels on the system 336 | kernels=$(dpkg --list | grep -E "(pve-kernel|proxmox-kernel)-[0-9].*" | grep -E "Kernel Image" | grep -vE "${latest_kernel%-pve}|series|transitional" | awk '{print $2}' | sed -n 's/\(pve\|proxmox\)-kernel-\(.*\)/\2/p' | sort -V) 337 | # List of kernels that will be removed (adds them as the script goes on) 338 | kernels_to_remove=() 339 | # Boot drive status 340 | boot_drive_status=$(get_drive_status ${boot_info[4]}) 341 | # Show space used, status and free space available 342 | printf "${bold}[*]${reset} Boot disk space used is ${bold}${boot_drive_status}${reset} at ${boot_info[4]}%% capacity (${boot_info[3]} free)\n" 343 | # For each kernel that was found via dpkg 344 | current_kernel_passed=false 345 | for kernel in $kernels 346 | do 347 | # Check if the kernel is already in the array 348 | if [[ " ${kernels_to_remove[@]} " =~ " $kernel " ]]; then 349 | continue # Skip adding it again 350 | fi 351 | # Only if not removing newer kernels and kernel matches the current kernel 352 | if [ "$(echo $kernel | grep "$current_kernel")" ]; then 353 | if [ "$remove_newer" == "false" ]; then 354 | break 355 | else 356 | current_kernel_passed=true 357 | continue 358 | fi 359 | # Add kernel to the list of removal since it is old 360 | else 361 | kernels_to_remove+=("$kernel") # Add the kernel to the array 362 | fi 363 | done 364 | # If remove_newer is set keep the last kernel installed as its newest 365 | # if [ "$remove_newer" == "true" ] && [ "$current_kernel_passed" == "true" ] && [ ${#kernels_to_remove[@]} -gt 0 ]; then 366 | # unset kernels_to_remove[-1] 367 | # fi 368 | # If keep_kernels is set we remove this number from the array to remove 369 | if [[ -n "$keep_kernels" ]] && [[ "$keep_kernels" =~ ^[0-9]+$ ]]; then 370 | if [ $keep_kernels -gt 0 ]; then 371 | printf "${bold}[*]${reset} The last ${bold}$keep_kernels${reset} kernel$([ "$keep_kernels" -eq 1 ] || echo 's') will be held back from being removed.\n" 372 | # Check if the number of kernels to keep is greater than or equal to the number of kernels in the array 373 | if [ "$keep_kernels" -ge "${#kernels_to_remove[@]}" ]; then 374 | # Set keep_kernels to the number of kernels in the array 375 | keep_kernels="${#kernels_to_remove[@]}" 376 | fi 377 | kernels_to_keep=("${kernels_to_remove[@]:${#kernels_to_remove[@]}-$keep_kernels:$keep_kernels}") 378 | kernels_to_remove=("${kernels_to_remove[@]::${#kernels_to_remove[@]}-$keep_kernels}") 379 | fi 380 | fi 381 | # Show kernels to be removed 382 | printf "${bold}[-]${reset} Searching for old PVE kernels on your system...\n" 383 | for kernel in "${kernels_to_remove[@]}" 384 | do 385 | printf " ${bold}${green}+${reset} \"$kernel\" added to the kernel remove list\n" 386 | done 387 | for kernel in "${kernels_to_keep[@]}" 388 | do 389 | printf " ${bold}${red}-${reset} \"$kernel\" is being held back from removal\n" 390 | done 391 | printf "${bold}[-]${reset} PVE kernel search complete!\n" 392 | # If there are no kernels to be removed then exit 393 | if [ ${#kernels_to_remove[@]} -eq 0 ]; then 394 | printf "${bold}[!]${reset} It appears there are no old PVE kernels on your system ⎦˚◡˚⎣\n" 395 | printf "${bold}[-]${reset} Good bye!\n" 396 | # Kernels found in removal list 397 | else 398 | num_to_remove=${#kernels_to_remove[@]} 399 | # Check if force removal was passed 400 | if [ $force_purge == true ]; then 401 | REPLY="y" 402 | # Ask the user if they want to remove the selected kernels found 403 | else 404 | printf "${bold}[!]${reset} Would you like to remove the ${bold}$num_to_remove${reset} selected PVE kernel$([ "$num_to_remove" -eq 1 ] || echo 's') listed above? [y/N]: " 405 | read -n 1 -r 406 | printf "\n" 407 | fi 408 | # User wishes to remove the kernels 409 | if [[ $REPLY =~ ^[Yy]$ ]]; then 410 | printf "${bold}[*]${reset} Removing $num_to_remove old PVE kernel$([ "$num_to_remove" -eq 1 ] || echo 's')...\n" 411 | for kernel in "${kernels_to_remove[@]}" 412 | do 413 | printf "${bold}[-]${reset} Removing kernel: $kernel..." 414 | # Purge the old kernels via apt and suppress output 415 | if [ "$dry_run" != "true" ]; then 416 | /usr/bin/apt purge -y pve-kernel-$kernel > /dev/null 2>&1 417 | /usr/bin/apt purge -y proxmox-kernel-$kernel > /dev/null 2>&1 418 | /usr/bin/apt purge -y pve-kernel-${kernel%-pve} > /dev/null 2>&1 419 | /usr/bin/apt purge -y proxmox-kernel-${kernel%-pve} > /dev/null 2>&1 420 | /usr/bin/apt purge -y pve-headers-${kernel%-pve} > /dev/null 2>&1 421 | /usr/bin/apt purge -y proxmox-headers-${kernel%-pve} > /dev/null 2>&1 422 | fi 423 | sleep 1 424 | printf "${bold}${green}DONE!${reset}\n" 425 | done 426 | printf "${bold}[*]${reset} Updating GRUB..." 427 | # Update grub after kernels are removed, suppress output 428 | if [ "$dry_run" != "true" ]; then 429 | /usr/sbin/update-grub > /dev/null 2>&1 430 | fi 431 | printf "${bold}${green}DONE!${reset}\n" 432 | # Get information about the /boot folder 433 | boot_info=($(echo $(df -Ph | grep /boot | tail -1) | sed 's/%//g')) 434 | # Show information about the /boot 435 | printf "${bold}[-]${reset} ${bold}Boot Disk:${reset} ${boot_info[4]}%% full [${boot_info[2]}/${boot_info[1]} used, ${boot_info[3]} free] \n" 436 | # Script finished successfully 437 | printf "${bold}[-]${reset} Have a nice $(timeGreeting) ⎦˚◡˚⎣\n" 438 | # User wishes to not remove the kernels above, exit 439 | else 440 | printf "\nExiting...\n" 441 | printf "See you later ⎦˚◡˚⎣\n" 442 | fi 443 | fi 444 | exit 0 445 | } 446 | 447 | # Function to check for updates 448 | check_for_update() { 449 | if [ "$check_for_updates" == "true" ] && [ "$force_purge" == "false" ]; then 450 | # Get latest version number 451 | local remote_version=$(curl -s -m 10 https://raw.githubusercontent.com/jordanhillis/pvekclean/master/version.txt | tr -d '\n' || echo "") 452 | # Unable to fetch remote version, so just skip the update check 453 | if [ -z "$remote_version" ]; then 454 | printf "${bold}[*]${reset} Failed to check for updates. Skipping update check.\n" 455 | return 456 | fi 457 | # Validate the remote_version format using a regex 458 | if [[ ! "$remote_version" =~ ^[0-9]+(\.[0-9]+)*$ ]]; then 459 | printf "${bold}[*]${reset} Invalid remote version format: ${bold}${orange}$remote_version${reset}. Skipping update check.\n" 460 | return 461 | fi 462 | # If version isn't the same 463 | if [ "$remote_version" != "$version" ]; then 464 | printf "*** A new version $remote_version is available! ***\n" 465 | printf "${bold}[*]${reset} Do you want to update? [y/N] " 466 | read -n 1 -r 467 | printf "\n" 468 | if [[ $REPLY =~ ^[Yy]$ ]]; then 469 | local updated_script=$(curl -s -m 10 https://raw.githubusercontent.com/jordanhillis/pvekclean/master/pvekclean.sh) 470 | # Check if the updated script contains the shebang line 471 | if [[ "$updated_script" == "#!/bin/bash"* ]]; then 472 | echo "$updated_script" > "$0" # Overwrite the current script 473 | printf "${bold}[*]${reset} Successfully updated to version $remote_version\n" 474 | exec "$0" "$@" 475 | else 476 | printf "${bold}[*]${reset} The updated script does not contain the expected shebang line.\n" 477 | printf "${bold}[*]${reset} Update aborted!\n" 478 | fi 479 | fi 480 | fi 481 | fi 482 | } 483 | 484 | timeGreeting() { 485 | h=$(date +%k) # Use %k to get the hour as a decimal number (no leading zero) 486 | ((h >= 5 && h < 12)) && echo "morning" && return 487 | ((h >= 12 && h < 17)) && echo "afternoon" && return 488 | ((h >= 17 && h < 21)) && echo "evening" && return 489 | echo "night" 490 | } 491 | 492 | main() { 493 | # Check for root 494 | check_root 495 | # Show header information 496 | header_info 497 | # Script usage 498 | show_usage 499 | # Show kernel information 500 | kernel_info 501 | # Check for updates 502 | check_for_update 503 | # Install program to /usr/local/sbin/ 504 | install_program 505 | } 506 | 507 | while [[ $# -gt 0 ]]; do 508 | case "$1" in 509 | -i|--install ) 510 | force_pvekclean_install=true 511 | main 512 | install_program 513 | ;; 514 | -r|--remove ) 515 | main 516 | uninstall_program 517 | ;; 518 | -s|--scheduler) 519 | main 520 | scheduler 521 | ;; 522 | -v|--version) 523 | version 524 | ;; 525 | -h|--help) 526 | main 527 | exit 0 528 | ;; 529 | -k|--keep) 530 | if [[ $# -gt 1 && "$2" =~ ^[0-9]+$ ]]; then 531 | keep_kernels="$2" 532 | shift 2 533 | continue 534 | else 535 | echo -e "${bold}Error:${reset} --keep/-k requires a number argument." 536 | exit 1 537 | fi 538 | ;; 539 | -f|--force) 540 | force_purge=true 541 | shift 542 | continue 543 | ;; 544 | -rn|--remove-newer) 545 | remove_newer=true 546 | shift 547 | continue 548 | ;; 549 | -d|--dry-run) 550 | dry_run=true 551 | shift 552 | continue 553 | ;; 554 | *) 555 | echo -e "${bold}Unknown option:${reset} $1" 556 | exit 1 557 | ;; 558 | esac 559 | shift 560 | done 561 | 562 | main 563 | pve_kernel_clean 564 | -------------------------------------------------------------------------------- /version.txt: -------------------------------------------------------------------------------- 1 | 2.0.2 2 | --------------------------------------------------------------------------------