.
682 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | 
4 |
5 | 
6 |
7 | [](https://GitHub.com/BassT23/Proxmox/releases/)
8 | [](https://github.com/BassT23/Proxmox/stargazers)
9 | [](https://github.com/BassT23/Proxmox/releases)
10 | [](https://discord.gg/nVpUg6BKn8)
11 |
12 | Proxmox® is a registered trademark of Proxmox Server Solutions GmbH.
13 |
14 | I am no member of the Proxmox Server Solutions GmbH. This is not an official program from Proxmox!
15 |
16 |
17 |
18 | > This is distributed in the hope that it will be useful, but
19 | > WITHOUT ANY WARRANTY; without even the implied warranty of
20 | > MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
21 | > See the GNU General Public License for more details.
22 |
23 |
24 |
25 | **IN CASE OF EMERGENCY, I HOPE YOU HAVE BACKUPS FROM YOUR MACHINES!**
26 |
27 | **YOU HAVE BEEN WARNED!**
28 |
29 |
30 |
31 | ### What does the script do:
32 | - The script makes system updates with apt/dnf/pacman/apk or yum on all nodes/LXCs and VMs (if VMs prepared for that)
33 | - Make a snapshot before update (if your storage support it - [look here](https://pve.proxmox.com/wiki/Storage)). If not supported, you can choose to make a real backup, but this must be enabled in `update.conf` by user (take long time!)
34 | - After all, the updater makes a little cleaning (like `apt autoremove`)
35 | - If the script detects "extra" installations, it could update this also. Look in config file, for that.
36 | - NEW: use your own scripts during update if you like. [Look here](https://github.com/BassT23/Proxmox/tree/develop#user-scripts)
37 |
38 | ### Features:
39 | - Update Proxmox VE (the host / all cluster nodes / all included LXCs and VMs)
40 | - Snapshot / Backup support (for Snapshot, your system must prepared for it)
41 | - Normal run is "Interactive" / Headless Mode can be run with `update -s`
42 | - Logging - location can be change in config file
43 | - Exit tracking, so you can send additional commands for finish or failure (edit files in `/etc/ultimate-updater/exit`)
44 | - [Config file](https://github.com/BassT23/Proxmox/tree/master#config-file)
45 | - Trim filesystem on ext4 nodes -
46 |
47 | Info can be found with `update -h`
48 |
49 | Changelog: [here](https://github.com/BassT23/Proxmox/blob/master/change.log)
50 |
51 | ##
52 | # Installation:
53 | In Proxmox GUI Host Shell or as root on proxmox host terminal:
54 | ```
55 | bash <(curl -s https://raw.githubusercontent.com/BassT23/Proxmox/master/install.sh)
56 | ```
57 |
58 | # Usage:
59 | - If you want to run the updater globally for all nodes/lxc/vm only run `update`
60 | - If you want to update only one specific lxc/vm run `update `
61 |
62 | ##
63 | ## Cluster-Mode preparation:
64 | **! For Cluster Installation, you only need to install on one Host !**
65 |
66 | The nodes need to know each other. For that please edit the `/etc/hosts` file on each node. Otherwise, you can use the GUI. (NODE -> System -> Hosts)
67 |
68 | Example add:
69 | ```
70 | 192.168.1.10 pve1
71 | 192.168.1.11 pve2
72 | 192.168.1.12 pve3
73 | ...
74 | ```
75 | IP and Name must match with node ip and its hostname.
76 | - IP can be found in node terminal with `hostname -I`
77 | - hostname can be found in node terminal with `hostname`
78 |
79 | After that make the fingerprints.
80 | The used sequence can be check, if you run `awk '/ring0_addr/{print $2}' "/etc/corosync/corosync.conf"` from the host, on which Proxmox-Updater is installed.
81 | So connect from first node (on which you install the Proxmox-Updater) to node2 with `ssh pve2`. Then from node2 `ssh pve3`, and so on.
82 |
83 | ## If you want to update the VMs also, you have two choices:
84 | 1. Use the "light and easy" QEMU option
85 |
86 | more infos here: [QEMU Guest Agent](https://pve.proxmox.com/wiki/Qemu-guest-agent)
87 |
88 | 2. Use ssh connection with Key-Based Authentication (a little more work, but nicer output and "extra" support)
89 |
90 | more infos here: [SSH Connection](https://github.com/BassT23/Proxmox/blob/master/ssh.md)
91 |
92 | # Update the script:
93 | `update -up`
94 |
95 | If update run into issue, please remove first with:
96 | ```
97 | bash <(curl -s https://raw.githubusercontent.com/BassT23/Proxmox/master/install.sh) uninstall
98 | ```
99 | and install new
100 |
101 | # Config File:
102 | The config file is stored under `/etc/ultimate-updater/update.conf`
103 |
104 | With this file, you can manage the updater. For example; if you don't want to update PiHole, comment the line out with #, or change `true` to `false`.
105 |
106 | - Host / LXC / VM
107 | - Headless Mode
108 | - Extra updates
109 | - "stopped" or "running" LXC/VM
110 | - "only" or "exclude" LXC/VM by ID
111 |
112 | # Extra Updates:
113 | If updater detects installation: (disable, if you want in `/etc/ultimate-updater/update.conf`)
114 | - PiHole
115 | - ioBroker
116 | - Pterodactyl
117 | - Octoprint
118 | - Docker Compose (v1 and v2)
119 |
120 | # User scripts
121 | How to use user scripts:
122 |
123 | In "/etc/ultimate-updater/scripts.d" create an folder for each LXC/VM who should use it like this:
124 | (000 is the example ID)
125 |
126 | /etc/ultimate-updater/scripts.d/000/
127 |
128 | here you can put in any script you like, which will be run during update also.
129 | !!! DON'T use free spaces in file name !!! ("file 1.sh" -> "file-1.sh")
130 |
131 | these files are used in the "extra update" section at the end of the LXC/VM
132 |
133 | # Welcome Screen:
134 | The Welcome Screen is an extra for you. It's optional!
135 |
136 | - The Welcome-Screen brings an update-checker with it. It check on 07am and 07pm for updates via crontab. The result will show up in Welcome-Screen (Only if updates are available).
137 | - The update-checker also uses the config file!
138 | - To force the check, you can run `/etc/ultimate-updater/check-updates.sh` in Terminal.
139 | - You can choose, if neofetch will be show also (if neofetch is not installed, script will make it automatically)
140 |
141 | # Beta Testing:
142 | If anybody wants to help with failure search, please test our beta (if available).
143 |
144 | Install beta update with `update beta -up`
145 | To go back to master, choose `update -up`
146 |
147 | # Q&A:
148 | [Discussion](https://github.com/BassT23/Proxmox/discussions/60)
149 |
150 | # Support:
151 | [](https://ko-fi.com/basst)
152 |
153 | # Contributors:
154 |
163 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Supported Versions
4 |
5 |
6 | | Version | Supported |
7 | | ------- | ------------------ |
8 | | 4.x | :white_check_mark: |
9 | | < 4.0 | :x: |
10 |
11 | ## Reporting a Vulnerability
12 |
13 | Issues can be reported [here](https://github.com/BassT23/Proxmox/issues) or per Discord
14 |
--------------------------------------------------------------------------------
/VMs/example:
--------------------------------------------------------------------------------
1 | ############################################################
2 | # - This file must be in the node who start the script. #
3 | # - If user is not root please look here: #
4 | # https://github.com/BassT23/Proxmox/blob/develop/ssh.md #
5 | ############################################################
6 |
7 | IP="111.111.111.111"
8 | USER="root"
9 | SSH_VM_PORT="22"
10 | # Time in secounds
11 | SSH_START_DELAY_TIME="45"
--------------------------------------------------------------------------------
/change.log:
--------------------------------------------------------------------------------
1 | Changelog:
2 | ==========
3 |
4 | **v4.4** (10.05.2025)
5 | - add symbolic
6 | - add user scripts folder
7 | - add "error hopping option" in config file
8 | - add LXC start delay option in config file
9 | - add "update" command for "Proxmox VE Helper-Scripts" in extra section
10 | - fix docker-compose v2 skipping
11 | - fix YUM Update - used now only for centos
12 | - fix VM shutdown errors
13 | - FreeBSD is disabled by default in config file
14 | - modify "update " - disable skip output
15 | - code cleaning
16 |
17 | **v4.3** (23.02.2025)
18 | - add freebsd VM (QEMU and SSH Connection)
19 | - add non root user update in VMs
20 | - add autoclean for debian based systems
21 | - add filesystem trim for ext4 systems
22 | - add curl for internet check
23 | - add env settings for arch in config file
24 | - add kernel kleaning (disabled by default in config file)
25 | - fix run for only one LXC/VM with `update `
26 | - fix ioBroker Upgrade
27 | - fix arch upgrade
28 | - fix START/STOP/WAITING for stopped VMs
29 | - fix compatibility with "old" ssh connection
30 | - fix IP output by nodes
31 | - fix fedora autoremove
32 | - fix missing kernel infos
33 | - fix template detection
34 | - fix internet check
35 | - modify config file - checker settings now on the buttom
36 | - modify version check - check now all branches
37 | - code cleaning
38 |
39 | **v4.2** (01.12.2024)
40 | - fix VM Template error
41 | - fix Unifi-Controller Container
42 | - fix FreeBSD VM error
43 | - fix docker compose
44 | - fix node hopping without welcome screen installed
45 | - rebuild waiting for VM start (could now be set in config file)
46 |
47 | **v4.1** (07.04.2024)
48 | - disable/enable Snapshots and Backups in config file
49 | - set, how many snapshots will be keep, in config file
50 | - delete old (Update_) snapshots
51 | - name changing ("Proxmox Updater" is now "The Ultimate Updater for Proxmox VE")
52 | - move program to correct folder system
53 | - edit config file, for better usage
54 | - update-check can now be better set (in config file)
55 | - support Docker Compose v1 and v2
56 | - bug fixing
57 |
58 | **v4.0** (18.12.2023)
59 | - fix loop through vms
60 | - fix new docker compose command
61 | - fix alpine issue (disable internet check for now)
62 | - add real snapshot support before update - if your storage setup support it
63 | - you can now choose, if neofetch will be installed during installation
64 | - code cleaning
65 |
66 | **v3.9** (07.11.2023)
67 | - make backup before update (must be enabled in config by user)
68 | - code cleaning
69 | - check internet connection on each LXC/VM
70 | - config file:
71 | - check for updates on excluded
72 | - include phased updates
73 | - make snapshot from LXC/VM before update (thanks to @elbim)
74 |
75 | **v3.8.5** (18.06.2023)
76 | - Skip node, if not reachable
77 | - check internet status
78 | - will always use master version
79 | - fix restore crontab, during uninstall
80 | - fix version info
81 |
82 | **v3.8.4** (01.04.2023)
83 | - Speed up start in cluster mode
84 | - Speed up installation
85 | - Bug fixing
86 | - Code cleaning
87 |
88 | **v3.8** (28.03.2023)
89 | - Add SSH Connection for VM - with extra update support
90 | - Add "Global Extra" in config file, for disable/enable Extra Updates
91 | - Info, if you are on beta/development version
92 | - Add "Status/Version" Info
93 | - Bug fixing
94 | - Code cleaning
95 | - Welcome Screen:
96 | - Ask for Welcome-Screen during install
97 | - Make update check during update now - not at the end
98 | - Fix time calculation
99 | - Add "reboot required"
100 |
101 | **v3.7** (09.03.2023)
102 | - Fixing code
103 | - Support "only" LXC/VM
104 | - Fix docker-compose update
105 | - VM-Update show errors
106 | - Add Welcome Screen with periodic update check
107 | - Need neofetch (will install automatic)
108 |
109 | **v3.6** (22.02.2023)
110 | - Add config file
111 | - Cleaning code
112 | - More colorful
113 | - Fixing logging
114 |
115 | **v3.4** (not released)
116 | - Add VM Update
117 | - Cleaning code
118 | - Fixed Docker Update
119 |
120 | **v3.3** (15.02.2023)
121 | - Add Update Info
122 | - Add extra Updates
123 | - Add version check by user
124 |
125 | **v3.1.1** (13.02.2023)
126 | - Fixed update failure with CentOS
127 |
128 | **v3.1** (12.02.2023)
129 | - Add better usage
130 | - Cleanup overall code
131 |
132 | **v3.0** (10.02.2023)
133 | - Implement single install url
134 |
135 | **v2.8** (09.02.2023)
136 | - Cleanup overall code
137 |
138 | **v2.7.1** (06.02.2023)
139 | - Small fixes
140 |
141 | **v2.7** (31.01.2023)
142 | - Add root check
143 | - Cleanup overall code
144 |
145 | **v2.6** (30.01.2023)
146 | - Cleanup overall code
147 | - Fix promt of update
148 | - Add updating package that been kept back
149 |
150 | **v2.5** (30.01.2023)
151 | - Added "Headless Mode" as option with `update -3` otherwise runs in "Interactive Mode"
152 |
153 | **v2.4** (29.01.2023)
154 | - Visual and Name changes
155 |
156 | **v2.3** (29.01.2023)
157 | - Update script itself with `update -u`
158 |
--------------------------------------------------------------------------------
/check-updates.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | #################
4 | # Check Updates #
5 | #################
6 |
7 | # shellcheck disable=SC1017
8 | # shellcheck disable=SC2034
9 |
10 | VERSION="1.7"
11 |
12 | #Variable / Function
13 | LOCAL_FILES="/etc/ultimate-updater"
14 | CONFIG_FILE="$LOCAL_FILES/update.conf"
15 |
16 | # Colors
17 | BL="\e[36m"
18 | OR="\e[1;33m"
19 | RD="\e[1;91m"
20 | GN="\e[1;92m"
21 | CL="\e[0m"
22 |
23 | ARGUMENTS () {
24 | while test $# -gt -0; do
25 | ARGUMENT="$1"
26 | case "$ARGUMENT" in
27 | -c)
28 | RICM=true
29 | ;;
30 | -u)
31 | RDU=true
32 | ;;
33 | chost)
34 | COMMAND=true
35 | OUTPUT_TO_FILE
36 | CHECK_HOST_ITSELF
37 | ;;
38 | ccontainer)
39 | COMMAND=true
40 | OUTPUT_TO_FILE
41 | CHECK_CONTAINER
42 | ;;
43 | cvm)
44 | COMMAND=true
45 | OUTPUT_TO_FILE
46 | CHECK_VM
47 | ;;
48 | host)
49 | COMMAND=true
50 | OUTPUT_TO_FILE
51 | if [[ "$WITH_HOST" == true ]]; then CHECK_HOST_ITSELF; fi
52 | if [[ "$WITH_LXC" == true ]]; then CONTAINER_CHECK_START; fi
53 | if [[ "$WITH_VM" == true ]]; then VM_CHECK_START; fi
54 | ;;
55 | cluster)
56 | COMMAND=true
57 | OUTPUT_TO_FILE
58 | HOST_CHECK_START
59 | ;;
60 | *)
61 | echo -e "\n${RD} Error: Got an unexpected argument \"$ARGUMENT\"${CL}";
62 | USAGE;
63 | exit 2;
64 | ;;
65 | esac
66 | shift
67 | done
68 | }
69 |
70 | # Usage
71 | USAGE () {
72 | echo -e "\nUsage: $0 {COMMAND}\n"
73 | echo -e "{COMMAND}:"
74 | echo -e "========="
75 | echo -e " host Host-Mode"
76 | echo -e " cluster Cluster-Mode\n"
77 | echo -e "Report issues at: \n"
78 | }
79 |
80 |
81 | READ_WRITE_CONFIG () {
82 | SSH_PORT=$(awk -F'"' '/^SSH_PORT=/ {print $2}' $CONFIG_FILE)
83 | WITH_HOST=$(awk -F'"' '/^CHECK_WITH_HOST=/ {print $2}' $CONFIG_FILE)
84 | WITH_LXC=$(awk -F'"' '/^CHECK_WITH_LXC=/ {print $2}' $CONFIG_FILE)
85 | WITH_VM=$(awk -F'"' '/^CHECK_WITH_VM=/ {print $2}' $CONFIG_FILE)
86 | RUNNING=$(awk -F'"' '/^CHECK_RUNNING_CONTAINER=/ {print $2}' $CONFIG_FILE)
87 | STOPPED=$(awk -F'"' '/^CHECK_STOPPED_CONTAINER=/ {print $2}' $CONFIG_FILE)
88 | EXCLUDED=$(awk -F'"' '/^EXCLUDE_UPDATE_CHECK=/ {print $2}' $CONFIG_FILE)
89 | ONLY=$(awk -F'"' '/^ONLY_UPDATE_CHECK=/ {print $2}' $CONFIG_FILE)
90 | }
91 |
92 | ## HOST ##
93 | # Host Check Start
94 | HOST_CHECK_START () {
95 | for HOST in $HOSTS; do
96 | CHECK_HOST "$HOST"
97 | done
98 | }
99 |
100 | # Host Check
101 | CHECK_HOST () {
102 | HOST=$1
103 | ssh "$HOST" -p "$SSH_PORT" mkdir -p $LOCAL_FILES
104 | scp $LOCAL_FILES/update.conf "$HOST":$LOCAL_FILES/update.conf >/dev/null 2>&1
105 | ssh "$HOST" -p "$SSH_PORT" 'bash -s' < "$0" -- "-c host"
106 | }
107 |
108 | CHECK_HOST_ITSELF () {
109 | apt-get update >/dev/null 2>&1
110 | SECURITY_APT_UPDATES=$(apt-get -s upgrade | grep -ci "^inst.*security" | tr -d '\n')
111 | NORMAL_APT_UPDATES=$(apt-get -s upgrade | grep -ci "^inst." | tr -d '\n')
112 | if [[ -f /var/run/reboot-required.pkgs ]]; then REBOOT_REQUIRED=true; fi
113 | if [[ $SECURITY_APT_UPDATES != 0 || $NORMAL_APT_UPDATES != 0 || $REBOOT_REQUIRED == true ]]; then
114 | echo -e "${GN}Host${CL} : ${GN}$HOSTNAME${CL}"
115 | fi
116 | if [[ $REBOOT_REQUIRED == true ]]; then echo -e "${OR} Reboot required${CL}"; fi
117 | if [[ $SECURITY_APT_UPDATES != 0 && $NORMAL_APT_UPDATES != 0 ]]; then
118 | echo -e "S: $SECURITY_APT_UPDATES / N: $NORMAL_APT_UPDATES"
119 | elif [[ $SECURITY_APT_UPDATES != 0 ]]; then
120 | echo -e "S: $SECURITY_APT_UPDATES / "
121 | elif [[ $NORMAL_APT_UPDATES != 0 ]]; then
122 | echo -e "N: $NORMAL_APT_UPDATES"
123 | fi
124 | }
125 |
126 | ## Container ##
127 | # Container Check Start
128 | CONTAINER_CHECK_START () {
129 | # Get the list of containers
130 | CONTAINERS=$(pct list | tail -n +2 | cut -f1 -d' ')
131 | # Loop through the containers
132 | if ! [[ -d $LOCAL_FILES/temp/ ]]; then mkdir $LOCAL_FILES/temp/; fi
133 | for CONTAINER in $CONTAINERS; do
134 | if [[ "$ONLY" == "" ]] && [[ "$EXCLUDED" =~ $CONTAINER ]]; then
135 | continue
136 | elif [[ "$ONLY" != "" ]] && ! [[ "$ONLY" =~ $CONTAINER ]]; then
137 | continue
138 | else
139 | STATUS=$(pct status "$CONTAINER")
140 | if [[ "$STATUS" == "status: stopped" && "$STOPPED" == true ]]; then
141 | # Start the container
142 | pct start "$CONTAINER"
143 | sleep 5
144 | CHECK_CONTAINER "$CONTAINER"
145 | # Stop the container
146 | pct shutdown "$CONTAINER"
147 | elif [[ "$STATUS" == "status: running" && "$RUNNING" == true ]]; then
148 | CHECK_CONTAINER "$CONTAINER"
149 | fi
150 | fi
151 | done
152 | rm -rf $LOCAL_FILES/temp/temp
153 | }
154 |
155 | # Container Check
156 | CHECK_CONTAINER () {
157 | if [[ "$RDU" != true ]]; then
158 | CONTAINER=$1
159 | else
160 | CONTAINER=$(awk -F'"' '/^CONTAINER=/ {print $2}' $LOCAL_FILES/temp/var)
161 | fi
162 | pct config "$CONTAINER" > $LOCAL_FILES/temp/temp
163 | OS=$(awk '/^ostype/' $LOCAL_FILES/temp/temp | cut -d' ' -f2)
164 | NAME=$(pct exec "$CONTAINER" hostname)
165 | if [[ "$OS" =~ ubuntu ]] || [[ "$OS" =~ debian ]] || [[ "$OS" =~ devuan ]]; then
166 | pct exec "$CONTAINER" -- bash -c "apt-get update" >/dev/null 2>&1
167 | SECURITY_APT_UPDATES=$(pct exec "$CONTAINER" -- bash -c "apt-get -s upgrade | grep -ci ^inst.*security | tr -d '\n'")
168 | NORMAL_APT_UPDATES=$(pct exec "$CONTAINER" -- bash -c "apt-get -s upgrade | grep -ci ^inst. | tr -d '\n'")
169 | # NOT_INSTALLED=$(apt-get -s upgrade | grep -ci "^inst.*not")
170 | if [[ "$SECURITY_APT_UPDATES" -gt 0 || "$NORMAL_APT_UPDATES" != 0 ]]; then
171 | echo -e "${GN}LXC ${BL}$CONTAINER${CL} : ${GN}$NAME${CL}"
172 | fi
173 | if [[ "$SECURITY_APT_UPDATES" -gt 0 && "$NORMAL_APT_UPDATES" != 0 ]]; then
174 | echo -e "S: $SECURITY_APT_UPDATES / N: $NORMAL_APT_UPDATES"
175 | elif [[ "$SECURITY_APT_UPDATES" -gt 0 ]]; then
176 | echo -e "S: $SECURITY_APT_UPDATES / "
177 | elif [[ "$NORMAL_APT_UPDATES" -gt 0 ]]; then
178 | echo -e "N: $NORMAL_APT_UPDATES"
179 | fi
180 | elif [[ "$OS" =~ fedora ]]; then
181 | pct exec "$CONTAINER" -- bash -c "dnf update" >/dev/null 2>&1
182 | UPDATES=$(pct exec "$CONTAINER" -- bash -c "dnf check-update | grep -Ec ' updates$'")
183 | if [[ "$UPDATES" -gt 0 ]]; then
184 | echo -e "${GN}LXC ${BL}$CONTAINER${CL} : ${GN}$NAME${CL}"
185 | echo -e "$UPDATES"
186 | fi
187 | elif [[ "$OS" =~ archlinux ]]; then
188 | pct exec "$CONTAINER" -- bash -c "pacman -Syu" >/dev/null 2>&1
189 | UPDATES=$(pct exec "$CONTAINER" -- bash -c "pacman -Qu | wc -l")
190 | if [[ "$UPDATES" -gt 0 ]]; then
191 | echo -e "${GN}LXC ${BL}$CONTAINER${CL} : ${GN}$NAME${CL}"
192 | echo -e "$UPDATES"
193 | fi
194 | elif [[ "$OS" =~ alpine ]]; then
195 | pct exec "$CONTAINER" -- ash -c "apk update" >/dev/null 2>&1
196 | else
197 | pct exec "$CONTAINER" -- bash -c "yum update" >/dev/null 2>&1
198 | UPDATES=$(pct exec "$CONTAINER" -- bash -c "yum -q check-update | wc -l")
199 | if [[ "$UPDATES" -gt 0 ]]; then
200 | echo -e "${GN}LXC ${BL}$CONTAINER${CL} : ${GN}$NAME${CL}"
201 | echo -e "$UPDATES"
202 | fi
203 | fi
204 | }
205 |
206 | ## VM ##
207 | # VM Check Start
208 | VM_CHECK_START () {
209 | # Get the list of VMs
210 | VMS=$(qm list | tail -n +2 | cut -c -10)
211 | # Loop through the VMs
212 | for VM in $VMS; do
213 | PRE_OS=$(qm config "$VM" | grep 'ostype:' | sed 's/ostype:\s*//')
214 | if [[ "$ONLY" == "" && "$EXCLUDED" =~ $VM ]]; then
215 | continue
216 | elif [[ "$ONLY" != "" ]] && ! [[ "$ONLY" =~ $VM ]]; then
217 | continue
218 | elif [[ "$PRE_OS" =~ w ]]; then
219 | continue
220 | else
221 | STATUS=$(qm status "$VM")
222 | if [[ "$STATUS" == "status: stopped" && "$STOPPED" == true ]]; then
223 | # Check if connection is available
224 | if [[ $(qm config "$VM" | grep 'agent:' | sed 's/agent:\s*//') == 1 ]] || [[ -f $LOCAL_FILES/VMs/"$VM" ]]; then
225 | # Start the VM
226 | qm start "$VM" >/dev/null 2>&1
227 | sleep 45
228 | CHECK_VM "$VM"
229 | # Stop the VM
230 | qm stop "$VM"
231 | fi
232 | elif [[ "$STATUS" == "status: running" && "$RUNNING" == true ]]; then
233 | CHECK_VM "$VM"
234 | fi
235 | fi
236 | done
237 | }
238 |
239 | # VM Check
240 | CHECK_VM () {
241 | if [[ "$RDU" != true ]]; then
242 | VM=$1
243 | else
244 | VM=$(awk -F'"' '/^VM=/ {print $2}' $LOCAL_FILES/temp/var)
245 | fi
246 | NAME=$(qm config "$VM" | grep 'name:' | sed 's/name:\s*//')
247 | if [[ -f $LOCAL_FILES/VMs/"$VM" ]]; then
248 | IP=$(awk -F'"' '/^IP=/ {print $2}' $LOCAL_FILES/VMs/"$VM")
249 | if ! (ssh "$IP" exit) >/dev/null 2>&1; then
250 | CHECK_VM_QEMU
251 | else
252 | OS_BASE=$(qm config "$VM" | grep ostype || true)
253 | if [[ "$OS_BASE" =~ l2 ]]; then
254 | KERNEL=$(qm guest cmd "$VM" get-osinfo 2>/dev/null | grep kernel-version || true)
255 | OS=$(ssh -q -p "$SSH_VM_PORT" "$USER"@"$IP" hostnamectl 2>/dev/null | grep System || true)
256 | # if [[ "$KERNEL" =~ FreeBSD ]]; then
257 | # ssh -t -q -p "$SSH_VM_PORT" -tt "$USER"@"$IP" pkg update
258 | # return
259 | # fi
260 | if [[ "$OS" =~ Ubuntu ]] || [[ "$OS" =~ Debian ]] || [[ "$OS" =~ Devuan ]]; then
261 | ssh "$IP" "apt-get update" >/dev/null 2>&1
262 | SECURITY_APT_UPDATES=$(ssh "$IP" "apt-get -s upgrade | grep -ci ^inst.*security")
263 | NORMAL_APT_UPDATES=$(ssh "$IP" "apt-get -s upgrade | grep -ci ^inst.")
264 | if ssh "$IP" stat /var/run/reboot-required.pkgs \> /dev/null 2\>\&1; then REBOOT_REQUIRED=true; fi
265 | if [[ "$SECURITY_APT_UPDATES" -gt 0 || "$NORMAL_APT_UPDATES" -gt 0 || "$REBOOT_REQUIRED" == true ]]; then
266 | echo -e "${GN}VM ${BL}$VM${CL} : ${GN}$NAME${CL}"
267 | fi
268 | if [[ "$REBOOT_REQUIRED" == true ]]; then echo -e "${OR} Reboot required${CL}"; fi
269 | if [[ "$SECURITY_APT_UPDATES" -gt 0 && "$NORMAL_APT_UPDATES" -gt 0 ]]; then
270 | echo -e "S: $SECURITY_APT_UPDATES / N: $NORMAL_APT_UPDATES"
271 | elif [[ "$SECURITY_APT_UPDATES" -gt 0 ]]; then
272 | echo -e "S: $SECURITY_APT_UPDATES / "
273 | elif [[ "$NORMAL_APT_UPDATES" -gt 0 ]]; then
274 | echo -e "N: $NORMAL_APT_UPDATES"
275 | fi
276 | elif [[ "$OS" =~ Fedora ]]; then
277 | ssh "$IP" "dnf -y update" >/dev/null 2>&1
278 | UPDATES=$(ssh "$IP" "dnf check-update| grep -Ec ' updates$'")
279 | if [[ "$UPDATES" -gt 0 ]]; then
280 | echo -e "${GN}VM ${BL}$VM${CL} : ${GN}$NAME${CL}"
281 | echo -e "$UPDATES"
282 | fi
283 | elif [[ "$OS" =~ Arch ]]; then
284 | UPDATES=$(ssh "$IP" "pacman -Qu | wc -l")
285 | if [[ "$UPDATES" -gt 0 ]]; then
286 | echo -e "${GN}VM ${BL}$VM${CL} : ${GN}$NAME${CL}"
287 | echo -e "$UPDATES"
288 | fi
289 | elif [[ "$OS" =~ Alpine ]]; then
290 | return
291 | elif [[ "$OS" =~ CentOS ]]; then
292 | UPDATES=$(ssh "$IP" "yum -q check-update | wc -l")
293 | if [[ "$UPDATES" -gt 0 ]]; then
294 | echo -e "${GN}VM ${BL}$VM${CL} : ${GN}$NAME${CL}"
295 | echo -e "$UPDATES"
296 | fi
297 | fi
298 | fi
299 | fi
300 | else
301 | CHECK_VM_QEMU
302 | fi
303 | }
304 |
305 | CHECK_VM_QEMU () {
306 | if qm guest exec "$VM" test >/dev/null 2>&1; then
307 | KERNEL=$(qm guest cmd "$VM" get-osinfo | grep kernel-version || true)
308 | OS=$(qm guest cmd "$VM" get-osinfo | grep name || true)
309 | # if [[ "$KERNEL" =~ FreeBSD ]]; then
310 | # qm guest exec "$VM" -- tcsh -c "pkg update"
311 | # return
312 | # fi
313 | if [[ "$OS" =~ Ubuntu ]] || [[ "$OS" =~ Debian ]] || [[ "$OS" =~ Devuan ]]; then
314 | qm guest exec "$VM" -- bash -c "apt-get update" >/dev/null 2>&1
315 | SECURITY_APT_UPDATES=$(qm guest exec "$VM" -- bash -c "apt-get -s upgrade | grep -ci ^inst.*security | tr -d '\n'" | tail -n +4 | head -n -1 | cut -c 18- | rev | cut -c 2- | rev)
316 | NORMAL_APT_UPDATES=$(qm guest exec "$VM" -- bash -c "apt-get -s upgrade | grep -ci ^inst. | tr -d '\n'" | tail -n +4 | head -n -1 | cut -c 18- | rev | cut -c 2- | rev)
317 | if [[ $(qm guest exec "$VM" -- bash -c "[ -f /var/run/reboot-required.pkgs ]" | grep exitcode) =~ 0 ]]; then REBOOT_REQUIRED=true; fi
318 | if [[ "$SECURITY_APT_UPDATES" -gt 0 || "$NORMAL_APT_UPDATES" -gt 0 || "$REBOOT_REQUIRED" == true ]]; then
319 | echo -e "${GN}VM ${BL}$VM${CL} : ${GN}$NAME${CL}"
320 | fi
321 | if [[ "$REBOOT_REQUIRED" == true ]]; then echo -e "${OR} Reboot required${CL}"; fi
322 | if [[ "$SECURITY_APT_UPDATES" -gt 0 && "$NORMAL_APT_UPDATES" -gt 0 ]]; then
323 | echo -e "S: $SECURITY_APT_UPDATES / N: $NORMAL_APT_UPDATES"
324 | elif [[ "$SECURITY_APT_UPDATES" -gt 0 ]]; then
325 | echo -e "S: $SECURITY_APT_UPDATES / "
326 | elif [[ "$NORMAL_APT_UPDATES" -gt 0 ]]; then
327 | echo -e "N: $NORMAL_APT_UPDATES"
328 | fi
329 | elif [[ "$OS" =~ Fedora ]]; then
330 | qm guest exec "$VM" -- bash -c "dnf -y update" >/dev/null 2>&1
331 | UPDATES=$(qm guest exec "$VM" -- bash -c "dnf check-update | grep -Ec ' updates$'" | tail -n +4 | head -n -1 | cut -c 18- | rev | cut -c 2- | rev)
332 | if [[ "$UPDATES" -gt 0 ]]; then
333 | echo -e "${GN}VM ${BL}$VM${CL} : ${GN}$NAME${CL}"
334 | echo -e "$UPDATES"
335 | fi
336 | elif [[ "$OS" =~ Arch ]]; then
337 | UPDATES=$(qm guest exec "$VM" -- bash -c "pacman -Qu | wc -l" | tail -n +4 | head -n -1 | cut -c 18- | rev | cut -c 2- | rev)
338 | if [[ "$UPDATES" -gt 0 ]]; then
339 | echo -e "${GN}VM ${BL}$VM${CL} : ${GN}$NAME${CL}"
340 | echo -e "$UPDATES"
341 | fi
342 | elif [[ "$OS" =~ Alpine ]]; then
343 | return
344 | elif [[ "$OS" =~ CentOS ]]; then
345 | UPDATES=$(qm guest exec "$VM" -- bash -c "yum -q check-update | wc -l" | tail -n +4 | head -n -1 | cut -c 18- | rev | cut -c 2- | rev)
346 | if [[ "$UPDATES" -gt 0 ]]; then
347 | echo -e "${GN}VM ${BL}$VM${CL} : ${GN}$NAME${CL}"
348 | echo -e "$UPDATES"
349 | fi
350 | fi
351 | fi
352 | }
353 |
354 | # Output to file
355 | OUTPUT_TO_FILE () {
356 | if [[ "$RDU" != true && "$RICM" != true ]]; then
357 | touch $LOCAL_FILES/check-output
358 | exec > >(tee $LOCAL_FILES/check-output)
359 | fi
360 | }
361 |
362 | # Check Cluster Mode
363 | if [[ -f /etc/corosync/corosync.conf ]]; then
364 | HOSTS=$(awk '/ring0_addr/{print $2}' "/etc/corosync/corosync.conf")
365 | MODE="Cluster"
366 | else
367 | MODE="Host"
368 | fi
369 |
370 | # Run
371 | if [[ "$(wget -q --spider http://google.com)" -eq 0 ]]; then
372 | READ_WRITE_CONFIG
373 | ARGUMENTS "$@"
374 | else
375 | echo -e "${OR} You are offline${CL}"
376 | exit 2
377 | fi
378 |
379 | # Run without commands (Automatic Mode)
380 | if [[ "$COMMAND" != true && "$RDU" == true ]]; then
381 | OUTPUT_TO_FILE
382 | CHECK_RUNNUNG_MACHINE
383 | elif [[ "$COMMAND" != true ]]; then
384 | OUTPUT_TO_FILE
385 | if [[ "$MODE" =~ Cluster ]]; then HOST_CHECK_START; else
386 | if [[ "$WITH_HOST" == true ]]; then CHECK_HOST_ITSELF; fi
387 | if [[ "$WITH_LXC" == true ]]; then CONTAINER_CHECK_START; fi
388 | if [[ "$WITH_VM" == true ]]; then VM_CHECK_START; fi
389 | fi
390 | fi
391 |
--------------------------------------------------------------------------------
/exit/error.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # Here you can execute commands, if script exited with errors
3 |
4 | echo -e "There are an error during update \
5 | \nPlease check output here or in logfile\n"
6 |
--------------------------------------------------------------------------------
/exit/passed.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | # Here you can execute commands, if script exited correctly
3 |
4 | echo -e "🎉 All fine, have a nice day\n"
5 |
--------------------------------------------------------------------------------
/install.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | ###########
4 | # Install #
5 | ###########
6 |
7 | # shellcheck disable=SC1017
8 | # shellcheck disable=SC2034
9 |
10 | VERSION="1.8.5"
11 |
12 | # Branch
13 | BRANCH="master"
14 |
15 | # Variable / Function
16 | LOCAL_FILES="/etc/ultimate-updater"
17 | TEMP_FOLDER="/root/Ultimate-Updater-Temp"
18 | SERVER_URL="https://raw.githubusercontent.com/BassT23/Proxmox/$BRANCH"
19 |
20 | #Colors
21 | BL="\e[36m"
22 | OR="\e[1;33m"
23 | RD="\e[1;91m"
24 | GN="\e[1;92m"
25 | CL="\e[0m"
26 |
27 | #Header
28 | HEADER_INFO () {
29 | clear
30 | echo -e "\n \
31 | https://github.com/BassT23/Proxmox\n"
32 | cat <<'EOF'
33 | The __ ______ _ __
34 | / / / / / /_(_)___ ___ ____ _/ /____
35 | / / / / / __/ / __ `__ \/ __ `/ __/ _ \
36 | / /_/ / / /_/ / / / / / / /_/ / /_/ __/
37 | \____/_/\__/_/_/ /_/ /_/\____/\__/\___/
38 | __ __ __ __
39 | / / / /___ ____/ /___ _/ /____ ____
40 | / / / / __ \/ __ / __ `/ __/ _ \/ __/
41 | / /_/ / /_/ / /_/ / /_/ / /_/ __/ /
42 | \____/ ____/\____/\____/\__/\___/_/
43 | /_/ for Proxmox VE
44 | EOF
45 | echo -e "\n \
46 | *** Install and/or Update *** \n \
47 | *** Version : $VERSION *** \n"
48 | CHECK_ROOT
49 | }
50 |
51 | #Check root
52 | CHECK_ROOT () {
53 | if [[ "$EUID" -ne 0 ]]; then
54 | echo -e >&2 "${RD}--- Please run this as root ---${CL}";
55 | exit 1
56 | fi
57 | }
58 |
59 | ARGUMENTS () {
60 | while test $# -gt -0; do
61 | ARGUMENT="$1"
62 | case "$ARGUMENT" in
63 | -h|--help)
64 | USAGE
65 | exit 0
66 | ;;
67 | status)
68 | STATUS
69 | ;;
70 | install)
71 | COMMAND=true
72 | INSTALL
73 | WELCOME_SCREEN
74 | EXIT
75 | ;;
76 | update)
77 | COMMAND=true
78 | UPDATE
79 | EXIT
80 | ;;
81 | uninstall)
82 | COMMAND=true
83 | UNINSTALL
84 | EXIT
85 | ;;
86 | welcome)
87 | WELCOME_SCREEN
88 | EXIT
89 | ;;
90 | *)
91 | echo -e "${RD}Error: Got an unexpected argument \"$ARGUMENT\"${CL}\n";
92 | USAGE;
93 | exit 1;
94 | ;;
95 | esac
96 | done
97 | }
98 |
99 | USAGE () {
100 | if [[ $SILENT != true ]]; then
101 | echo -e "Usage: $0 {COMMAND}\n"
102 | echo -e "{COMMAND}:"
103 | echo -e "=========="
104 | echo -e " -h --help Show help menu"
105 | echo -e " status Check current installation status"
106 | echo -e " install Install The Ultimate Updater"
107 | echo -e " welcome Install or Uninstall Welcome Screen"
108 | echo -e " uninstall Uninstall The Ultimate Updater"
109 | echo -e " update Update The Ultimate Updater\n"
110 | echo -e "Report issues at: \n"
111 | fi
112 | }
113 |
114 | IS_INSTALLED () {
115 | if [ -f "/usr/local/sbin/update" ]; then
116 | true
117 | else
118 | false
119 | fi
120 | }
121 |
122 | STATUS () {
123 | if [[ $SILENT != true ]]; then
124 | echo -e "The Ultimate Updater"
125 | if IS_INSTALLED; then
126 | echo -e "Status: ${GN}present${CL}\n"
127 | else
128 | echo -e "Status: ${RD}not present${CL}\n"
129 | fi
130 | fi
131 | if IS_INSTALLED; then exit 0; else exit 1; fi
132 | }
133 |
134 | INFORMATION () {
135 | if [[ -d /root/Proxmox-Updater/ ]]; then
136 | echo -e "\n${RD} --- ATTENTION! ---\n Because of name and directory changing, you will need an reboot of the node, after the update\n\n${BL} Do you want to proceed?${CL}"
137 | read -p " Type [Y/y] or Enter for yes - anything else will exit: " -r
138 | if ! [[ $REPLY =~ ^[Yy]$ || $REPLY = "" ]]; then
139 | exit 1
140 | fi
141 | fi
142 | }
143 |
144 | OLD_FILESYSTEM_CHECK () {
145 | if [[ -d /root/Proxmox-Updater/ ]]; then
146 | mv /root/Proxmox-Updater/ $LOCAL_FILES/
147 | if [[ -f /etc/update-motd.d/01-welcome-screen ]]; then
148 | mv /etc/crontab /etc/crontab.bak_name_change
149 | cp /etc/crontab.bak /etc/crontab
150 | echo "00 07,19 * * * root $LOCAL_FILES/check-updates.sh" >> /etc/crontab
151 | fi
152 | fi
153 | if [[ -d /root/Ultimative-Updater/ ]]; then
154 | if [[ -f /etc/update-motd.d/01-welcome-screen ]]; then
155 | mv /etc/crontab /etc/crontab.bak_name_change
156 | cp /etc/crontab.bak /etc/crontab
157 | echo "00 07,19 * * * root $LOCAL_FILES/check-updates.sh" >> /etc/crontab
158 | fi
159 | fi
160 | if [ -d "/root/Ultimative-Update-Scripts" ]; then
161 | echo -e "${RD}Ultimate-Updater has changed directory's, so the old directory\n\
162 | /root/Update-Scripts will be delete.${CL}\n\
163 | ${OR}Is it OK for you, or want to backup your files first?${CL}\n"
164 | read -p "Type [Y/y] for DELETE - anything else will exit: " -r
165 | if [[ $REPLY =~ ^[Yy]$ ]]; then
166 | rm -rf /root/Update-Proxmox-Scripts || true
167 | bash <(curl -s $SERVER_URL/install.sh) update
168 | else
169 | exit 0
170 | fi
171 | fi
172 | # Delete old files (old filesystem)
173 | rm -rf /etc/update-motd.d/01-updater || true
174 | rm -rf /etc/update-motd.d/01-updater.bak || true
175 | # Check an renew to new structure
176 | if [[ -f /usr/local/bin/update ]] && [[ ! -f /usr/local/sbin/update ]]; then
177 | curl -s -L https://raw.githubusercontent.com/BassT23/Proxmox/$BRANCH/update.sh > $LOCAL_FILES/update.sh
178 | chmod 750 $LOCAL_FILES/update.sh
179 | ln -sf $LOCAL_FILES/update.sh /usr/local/sbin/update
180 | rm /usr/local/bin/update
181 | NEED_REBOOT=true
182 | fi
183 | }
184 |
185 | INSTALL () {
186 | echo -e "\n${BL}[Info]${GN} Installing The Ultimate Updater${CL}\n"
187 | if [ -f "/usr/local/sbin/update" ]; then
188 | echo -e "${OR}The Ultimate Updater is already installed.${CL}"
189 | read -p "Should I update for you? Type [Y/y] or Enter for yes - anything else will exit: " -r
190 | if [[ $REPLY =~ ^[Yy]$ || $REPLY = "" ]]; then
191 | bash <(curl -s $SERVER_URL/install.sh) update
192 | else
193 | echo -e "${OR}\nBye\n${CL}"
194 | exit 0
195 | fi
196 | else
197 | mkdir -p $LOCAL_FILES/exit
198 | mkdir -p $LOCAL_FILES/VMs
199 | mkdir -p $LOCAL_FILES/scripts.d/000
200 | # Download latest release
201 | if ! [[ -d $TEMP_FOLDER ]];then mkdir $TEMP_FOLDER; fi
202 | curl -s https://api.github.com/repos/BassT23/Proxmox/releases/latest | grep "browser_download_url" | cut -d : -f 2,3 | tr -d \" | wget -i - -q -O $TEMP_FOLDER/ultimate-updater.tar.gz
203 | tar -zxf $TEMP_FOLDER/ultimate-updater.tar.gz -C $TEMP_FOLDER
204 | rm -rf $TEMP_FOLDER/ultimate-updater.tar.gz || true
205 | TEMP_FILES=$TEMP_FOLDER
206 | # Copy files
207 | cp "$TEMP_FILES"/update.sh $LOCAL_FILES/update.sh
208 | chmod 750 $LOCAL_FILES/update.sh
209 | ln -sf $LOCAL_FILES/update.sh /usr/local/sbin/update
210 | cp "$TEMP_FILES"/VMs/example $LOCAL_FILES/VMs/example
211 | cp "$TEMP_FILES"/exit/* $LOCAL_FILES/exit/
212 | chmod -R +x "$LOCAL_FILES"/exit/*.sh
213 | cp "$TEMP_FILES"/scripts.d/000/* $LOCAL_FILES/scripts.d/000/
214 | cp "$TEMP_FILES"/update-extras.sh $LOCAL_FILES/update-extras.sh
215 | cp "$TEMP_FILES"/update.conf $LOCAL_FILES/update.conf
216 | echo -e "${OR}Finished. Run The Ultimate Updater with 'update'.${CL}"
217 | echo -e "For infos and warnings please check the readme under \n"
218 | echo -e "${OR}Also want to install the Welcome-Screen?${CL}"
219 | read -p "Type [Y/y] or Enter for yes - anything else will exit: " -r
220 | if [[ $REPLY =~ ^[Yy]$ || $REPLY = "" ]]; then
221 | WELCOME_SCREEN_INSTALL
222 | fi
223 | rm -rf $TEMP_FOLDER || true
224 | fi
225 | }
226 |
227 | UPDATE () {
228 | INFORMATION
229 | OLD_FILESYSTEM_CHECK
230 | if [ -f "/usr/local/sbin/update" ]; then
231 | # Update
232 | echo -e "\n${BL}[Info]${GN} Updating script ...${CL}\n"
233 | # Cleaning
234 | rm -rf "$TEMP_FOLDER" || true
235 | # Download files
236 | if ! [[ -d $TEMP_FOLDER ]]; then mkdir $TEMP_FOLDER; fi
237 | if [[ "$BRANCH" == master ]]; then
238 | curl -s https://api.github.com/repos/BassT23/Proxmox/releases/latest | grep "browser_download_url" | cut -d : -f 2,3 | tr -d \" | wget -i - -q -O $TEMP_FOLDER/ultimate-updater.tar.gz
239 | elif [[ "$BRANCH" == beta ]]; then
240 | curl -s -L https://github.com/BassT23/Proxmox/tarball/beta > $TEMP_FOLDER/ultimate-updater.tar.gz
241 | elif [[ "$BRANCH" == develop ]]; then
242 | curl -s -L https://github.com/BassT23/Proxmox/tarball/develop > $TEMP_FOLDER/ultimate-updater.tar.gz
243 | fi
244 | tar -zxf $TEMP_FOLDER/ultimate-updater.tar.gz -C $TEMP_FOLDER
245 | rm -rf $TEMP_FOLDER/ultimate-updater.tar.gz || true
246 | if [[ "$BRANCH" == master ]]; then
247 | TEMP_FILES=$TEMP_FOLDER
248 | else
249 | TEMP_FILES=$TEMP_FOLDER/$(ls $TEMP_FOLDER)
250 | fi
251 | # Copy files
252 | mv "$TEMP_FILES"/update.sh $LOCAL_FILES/update.sh
253 | chmod 750 $LOCAL_FILES/update.sh
254 | mv "$TEMP_FILES"/VMs/example $LOCAL_FILES/VMs/example
255 | if ! [[ -d "$LOCAL_FILES"/scripts.d/ ]]; then
256 | mkdir -p $LOCAL_FILES/scripts.d/000
257 | mv "$TEMP_FILES"/scripts.d/000/* $LOCAL_FILES/scripts.d/000/
258 | rm -rf "$TEMP_FILES"/scripts.d/ || true
259 | else
260 | rm -rf "$TEMP_FILES"/scripts.d/ || true
261 | fi
262 | if [[ -f /etc/update-motd.d/01-welcome-screen ]]; then
263 | mv "$TEMP_FILES"/welcome-screen.sh /etc/update-motd.d/01-welcome-screen
264 | chmod +x /etc/update-motd.d/01-welcome-screen
265 | mv "$TEMP_FILES"/check-updates.sh $LOCAL_FILES/check-updates.sh
266 | chmod +x $LOCAL_FILES/check-updates.sh
267 | else
268 | rm -rf "$TEMP_FILES"/welcome-screen.sh || true
269 | rm -rf "$TEMP_FILES"/check-updates.sh || true
270 | fi
271 | # Check if files are different
272 | rm -rf "$TEMP_FILES"/.github || true
273 | rm -rf "$TEMP_FILES"/VMs || true
274 | rm -rf "$TEMP_FILES"/LICENSE || true
275 | rm -rf "$TEMP_FILES"/README.md || true
276 | rm -rf "$TEMP_FILES"/change.log || true
277 | rm -rf "$TEMP_FILES"/install.sh || true
278 | rm -rf "$TEMP_FILES"/ssh.md || true
279 | rm -rf "$TEMP_FILES"/CODE_OF_CONDUCT.md || true
280 | rm -rf "$TEMP_FILES"/SECURITY.md || true
281 | chmod -R +x "$TEMP_FILES"/exit/*.sh
282 | cd "$TEMP_FILES"
283 | FILES="*.* **/*.*"
284 | for FILE in $FILES
285 | do
286 | CHECK_DIFF
287 | done
288 | rm -rf $TEMP_FOLDER || true
289 | echo -e "${GN}The Ultimate Updater updated successfully.${CL}"
290 | if [[ "$BRANCH" != master ]]; then echo -e "${OR} Installed: $BRANCH version${CL}"; fi
291 | echo -e "For infos and warnings please check the readme under \n"
292 | if [[ $NEED_REBOOT == true ]]; then
293 | echo -e "${RD} Please reboot, to make The Ultimative Updater workable\n${CL}"
294 | fi
295 | else
296 | # Install, because no installation found
297 | echo -e "${RD}The Ultimate Updater is not installed.\n\n${OR}Would you like to install it?${CL}"
298 | read -p "Type [Y/y] or Enter for yes - anything else will exit: " -r
299 | if [[ $REPLY =~ ^[Yy]$ || $REPLY = "" ]]; then
300 | bash <(curl -s $SERVER_URL/install.sh)
301 | else
302 | echo -e "\n\nBye\n"
303 | exit 0
304 | fi
305 | fi
306 | }
307 |
308 | CHECK_DIFF () {
309 | if ! cmp -s "$TEMP_FILES"/"$FILE" "$LOCAL_FILES"/"$FILE"; then
310 | echo -e "The file ${OR}$FILE${CL}\n \
311 | was modified (by you or by a script) since installation.\n \
312 | What would you like to do about it ? Your options are:\n \
313 | Y or y : install the package maintainer's version (old file will be saved as '$FILE.bak')\n \
314 | N or n : keep your currently-installed version\n \
315 | S or s : show the differences between the versions\n \
316 | The default action is to install new version and backup current file."
317 | read -p "*** $FILE (Y/y/N/n/S/s) [default=Y] ?" -r
318 | if [[ $REPLY =~ ^[Yy]$ || $REPLY = "" ]]; then
319 | echo -e "\n${BL}[Info]${GN} Installed server version and backed up old file${CL}\n"
320 | cp -f "$LOCAL_FILES"/"$FILE" "$LOCAL_FILES"/"$FILE".bak
321 | mv "$TEMP_FILES"/"$FILE" "$LOCAL_FILES"/"$FILE"
322 | elif [[ $REPLY =~ ^[Nn]$ ]]; then
323 | echo -e "\n${BL}[Info]${GN} Kept old file${CL}\n"
324 | elif [[ $REPLY =~ ^[Ss]$ ]]; then
325 | echo
326 | set +e
327 | diff "$TEMP_FILES"/"$FILE" "$LOCAL_FILES/$FILE"
328 | set -e
329 | echo -e "\n What would you like to do about it ? Your options are:\n \
330 | Y or y : install the package maintainer's version (old file will be saved as '$FILE.bak')\n \
331 | N or n : keep your currently-installed version\n \
332 | The default action is to install new version and backup current file."
333 | read -p "*** $FILE (Y/y/N/n) [default=Y] ?" -r
334 | if [[ $REPLY =~ ^[Yy]$ || $REPLY = "" ]]; then
335 | echo -e "\n${BL}[Info]${GN} Installed server version and backed up old file${CL}\n"
336 | cp -f "$LOCAL_FILES"/"$FILE" "$LOCAL_FILES"/"$FILE".bak
337 | mv "$TEMP_FILES"/"$FILE" "$LOCAL_FILES"/"$FILE"
338 | elif [[ $REPLY =~ ^[Nn]$ ]]; then
339 | echo -e "\n${BL}[Info]${GN} Kept old file${CL}\n"
340 | fi
341 | else
342 | echo -e "\n${BL}[Info]${OR} Skip this file${CL}\n"
343 | fi
344 | fi
345 | }
346 |
347 | WELCOME_SCREEN () {
348 | if [[ $COMMAND != true ]]; then
349 | echo -e "\n${BL}[Info]${GN} Installing The Ultimate Updater Welcome-Screen${CL}\n"
350 | if ! [[ -d $TEMP_FOLDER ]];then mkdir $TEMP_FOLDER; fi
351 | curl -s $SERVER_URL/welcome-screen.sh > $TEMP_FOLDER/welcome-screen.sh
352 | curl -s $SERVER_URL/check-updates.sh > $TEMP_FOLDER/check-updates.sh
353 | if ! [[ -f "/etc/update-motd.d/01-welcome-screen" && -x "/etc/update-motd.d/01-welcome-screen" ]]; then
354 | echo -e "${OR} Welcome-Screen is not installed${CL}\n"
355 | read -p "Would you like to install it also? Type [Y/y] or Enter for yes - anything else will skip: " -r
356 | if [[ $REPLY =~ ^[Yy]$ || $REPLY = "" ]]; then
357 | WELCOME_SCREEN_INSTALL
358 | fi
359 | else
360 | echo -e "${OR} Welcome-Screen is already installed${CL}\n"
361 | read -p "Would you like to uninstall it? Type [Y/y] for yes - anything else will skip: " -r
362 | if [[ $REPLY =~ ^[Yy]$ ]]; then
363 | rm -rf /etc/update-motd.d/01-welcome-screen || true
364 | rm -rf /etc/motd || true
365 | if [[ -f /etc/motd.bak ]]; then mv /etc/motd.bak /etc/motd; fi
366 | #restore old crontab with info output
367 | mv /etc/crontab /etc/crontab.bak2
368 | mv /etc/crontab.bak /etc/crontab
369 | mv /etc/crontab.bak2 /etc/crontab.bak
370 | echo -e "\n${BL} Welcome-Screen uninstalled${CL}\n\
371 | ${BL} crontab file restored (old one backed up as crontab.bak)${CL}\n"
372 | fi
373 | fi
374 | rm -rf $TEMP_FOLDER || true
375 | fi
376 | }
377 |
378 | WELCOME_SCREEN_INSTALL () {
379 | if [[ -f /etc/motd ]];then mv /etc/motd /etc/motd.bak; fi
380 | touch /etc/motd
381 | cp /etc/crontab /etc/crontab.bak
382 | cp $TEMP_FOLDER/welcome-screen.sh /etc/update-motd.d/01-welcome-screen
383 | cp $TEMP_FOLDER/check-updates.sh $LOCAL_FILES/check-updates.sh
384 | chmod +x /etc/update-motd.d/01-welcome-screen
385 | chmod +x $LOCAL_FILES/check-updates.sh
386 | if ! [[ -f $LOCAL_FILES/check-output ]]; then touch $LOCAL_FILES/check-output; fi
387 | if ! grep -q "check-updates.sh" /etc/crontab; then
388 | echo "00 07,19 * * * root $LOCAL_FILES/check-updates.sh" >> /etc/crontab
389 | fi
390 | echo -e "${OR} with or without neofetch?${CL}"
391 | read -p " Type [Y/y] or Enter for install neofetch - anything else will install without neofetch: " -r
392 | if [[ $REPLY =~ ^[Yy]$ || $REPLY = "" ]]; then
393 | if ! [[ -f /usr/bin/neofetch ]]; then apt-get install neofetch -y; fi
394 | echo -e "\n${GN} Welcome-Screen installed with neofetch${CL}"
395 | else
396 | echo -e "\n${GN} Welcome-Screen installed without neofetch${CL}"
397 | fi
398 | }
399 |
400 | UNINSTALL () {
401 | if [ -f /usr/local/sbin/update ]; then
402 | echo -e "\n${BL}[Info]${GN} Uninstall The Ultimate Updater${CL}\n"
403 | echo -e "${RD}Really want to remove The Ultimate Updater?${CL}"
404 | read -p "Type [Y/y] for yes - anything else will exit: " -r
405 | if [[ $REPLY =~ ^[Yy]$ ]]; then
406 | rm /usr/local/sbin/update
407 | rm -r $LOCAL_FILES
408 | if [[ -f /etc/update-motd.d/01-welcome-screen ]]; then
409 | rm -rf /etc/update-motd.d/01-welcome-screen
410 | rm -rf /etc/motd
411 | if [[ -f /etc/motd.bak ]]; then
412 | mv /etc/motd.bak /etc/motd
413 | fi
414 | mv /etc/crontab /etc/crontab.bak2
415 | mv /etc/crontab.bak /etc/crontab
416 | mv /etc/crontab.bak2 /etc/crontab.bak
417 | fi
418 | echo -e "\n\n${BL} The Ultimate Updater has gone${CL}\n\
419 | ${BL} crontab file restored (old one backed up as crontab.bak)${CL}\n"
420 | exit 0
421 | fi
422 | else
423 | echo -e "${RD}The Ultimate Updater is not installed.${CL}\n"
424 | fi
425 | }
426 |
427 | #Error/Exit
428 | set -e
429 | EXIT () {
430 | EXIT_CODE=$?
431 | # Install Finish
432 | if [[ $EXIT_CODE -lt 2 ]]; then
433 | exit 0
434 | elif [[ $EXIT_CODE != "0" ]]; then
435 | rm -rf $TEMP_FOLDER || true
436 | echo -e "${RD}Error during install --- Exit Code: $EXIT_CODE${CL}\n"
437 | fi
438 | }
439 |
440 | # Exit Code
441 | trap EXIT EXIT
442 |
443 | #Install
444 | HEADER_INFO
445 | ARGUMENTS "$@"
446 |
447 | # Run without commands
448 | if [[ $COMMAND != true ]]; then
449 | INSTALL
450 | fi
451 |
--------------------------------------------------------------------------------
/scripts.d/000/example.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | echo "make something spezial :)"
4 |
5 | exit 0
--------------------------------------------------------------------------------
/ssh.md:
--------------------------------------------------------------------------------
1 | # SSH Key-Based Authentication
2 |
3 | ## VM need an static IP Address - Random IP will not work with the script.
4 |
5 | ### IN VM:
6 | (Example for Debian Based Systems)
7 |
8 | - install ssh server (if not installed):
9 | `sudo apt-get install openssh-server`
10 |
11 | - Set root password (if not made):
12 | `sudo passwd root`
13 |
14 | - Edit the sshd_config file in `/etc/ssh/sshd_config`:
15 |
16 | `sudo nano /etc/ssh/sshd_config`
17 |
18 | - Add a line in the Authentication section of the file that says `PermitRootLogin yes`. This line may already exist and be commented out with a "#". In this case, remove the "#":
19 | ```
20 | # Authentication:
21 | #LoginGraceTime 2m
22 | PermitRootLogin yes
23 | #StrictModes yes
24 | ```
25 |
26 | - Save the updated `/etc/ssh/sshd_config` file
27 |
28 | - Restart the SSH server:
29 | `sudo service sshd restart`
30 |
31 |
32 | ### IN HOST who hosted the VM:
33 | - Copy ssh key to VM:
34 | `ssh-copy-id -i /root/.ssh/id_rsa.pub root@`
35 |
36 | - or, if used user is not root:
37 | `ssh-copy-id -i /root/.ssh/id_rsa.pub @`
38 |
39 |
40 | ### IN HOST where ultimate-updater start:
41 | - create one file per VM in `/etc/ultimate-updater/VMs/` with content:
42 |
43 | ```
44 | IP="111.111.111.111" # use the IP from the VM!
45 | USER="root"
46 | SSH_VM_PORT="22"
47 | SSH_START_DELAY_TIME="45"
48 | ```
49 | (IP can be found in VM with command: `hostname -I`)
50 |
51 |
52 | ## If user is NOT root, you need to prepare the user, to run admin commands - like `apt` - but user MUST be part of group `sudo`
53 |
54 | Example for Ubuntu/Debian - with sudo (change in VM):
55 |
56 | - `sudo visudo`
57 | - add this to file:
58 |
59 | `%sudo ALL=(root) NOPASSWD: /usr/bin/apt-get update, /usr/bin/apt-get upgrade -y, /usr/bin/apt-get --purge autoremove -y, /usr/bin/apt-get autoclean -y`
60 | - save and exit file
61 |
62 | Sources:
63 | - easy, but unsafe -> look [here](https://askubuntu.com/questions/74054/run-apt-get-without-sudo)
64 | - for more safety -> look [here](https://stackoverflow.com/questions/73397309/how-do-i-enable-passwordless-sudo-for-all-options-for-a-command):
65 |
--------------------------------------------------------------------------------
/update-extras.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | #################
4 | # Update-Extras #
5 | #################
6 |
7 | # shellcheck disable=SC1017
8 | # shellcheck disable=SC2034
9 |
10 | VERSION="2.0"
11 |
12 | # Variables
13 | CONFIG_FILE="/etc/ultimate-updater/update.conf"
14 | PIHOLE=$(awk -F'"' '/^PIHOLE=/ {print $2}' $CONFIG_FILE)
15 | IOBROKER=$(awk -F'"' '/^IOBROKER=/ {print $2}' $CONFIG_FILE)
16 | PTERODACTYL=$(awk -F'"' '/^PTERODACTYL=/ {print $2}' $CONFIG_FILE)
17 | OCTOPRINT=$(awk -F'"' '/^OCTOPRINT=/ {print $2}' $CONFIG_FILE)
18 | DOCKER_COMPOSE=$(awk -F'"' '/^DOCKER_COMPOSE=/ {print $2}' $CONFIG_FILE)
19 | COMPOSE_PATH=$(awk -F'"' '/^COMPOSE_PATH=/ {print $2}' $CONFIG_FILE)
20 | INCLUDE_HELPER_SCRIPTS=$(awk -F'"' '/^INCLUDE_HELPER_SCRIPTS=/ {print $2}' $CONFIG_FILE)
21 |
22 | # PiHole
23 | if [[ -f "/usr/local/bin/pihole" && $PIHOLE == true ]]; then
24 | echo -e "\n*** Updating PiHole ***\n"
25 | /usr/local/bin/pihole -up
26 | fi
27 |
28 | # ioBroker
29 | if [[ -d "/opt/iobroker" && $IOBROKER == true ]]; then
30 | echo -e "\n*** Updating ioBroker ***\n"
31 | echo "*** Stop ioBroker ***" && sudo -u iobroker bash -c "iob stop" && echo
32 | echo "*** Update/Upgrade ioBroker ***" && sudo -u iobroker bash -c "iob update" && sudo -u iobroker bash -c "iob upgrade -y" && sudo -u iobroker bash -c "iob upgrade self -y" && echo
33 | echo "*** Start ioBroker ***" && sudo -u iobroker bash -c "iob start" && echo
34 | if [[ -d "/opt/iobroker/iobroker-data/radar2.admin" ]]; then
35 | setcap cap_net_admin,cap_net_raw,cap_net_bind_service=+eip "$(eval readlink -f '$(which arp-scan)')"
36 | setcap cap_net_admin,cap_net_raw,cap_net_bind_service=+eip "$(eval readlink -f '$(which node)')"
37 | setcap cap_net_admin,cap_net_raw,cap_net_bind_service=+eip "$(eval readlink -f '$(which arp)')"
38 | setcap cap_net_admin,cap_net_raw,cap_net_bind_service=+eip "$(eval readlink -f '$(which hcitool)')"
39 | setcap cap_net_admin,cap_net_raw,cap_net_bind_service=+eip "$(eval readlink -f '$(which hciconfig)')"
40 | setcap cap_net_admin,cap_net_raw,cap_net_bind_service=+eip "$(eval readlink -f '$(which l2ping)')"
41 | fi
42 | fi
43 |
44 | # Pterodactyl
45 | if [[ -d "/var/www/pterodactyl" && $PTERODACTYL == true ]]; then
46 | echo -e "\n*** Updating Pterodactyl ***\n"
47 | cd /var/www/pterodactyl || exit
48 | php artisan down
49 | curl -L https://github.com/pterodactyl/panel/releases/latest/download/panel.tar.gz | tar -xzv
50 | chmod -R 755 storage/* bootstrap/cache
51 | composer install --no-dev --optimize-autoloader
52 | php artisan view:clear
53 | php artisan config:clear
54 | php artisan migrate --seed --force
55 | os=$(hostnamectl | grep System)
56 | if [[ $os =~ CentOS ]]; then
57 | # If using NGINX on CentOS:
58 | if id -u "nginx" >/dev/null 2>&1; then
59 | chown -R nginx:nginx /var/www/pterodactyl/*
60 | # If using Apache on CentOS
61 | elif id -u "apache" >/dev/null 2>&1; then
62 | chown -R apache:apache /var/www/pterodactyl/*
63 | fi
64 | else
65 | # If using NGINX or Apache (not on CentOS):
66 | chown -R www-data:www-data /var/www/pterodactyl/*
67 | fi
68 | php artisan queue:restart
69 | php artisan up
70 | #Upgrading Wings
71 | systemctl stop wings
72 | curl -L -o /usr/local/bin/wings "https://github.com/pterodactyl/wings/releases/latest/download/wings_linux_$([[ "$(uname -m)" == "x86_64" ]] && echo "amd64" || echo "arm64")"
73 | chmod u+x /usr/local/bin/wings
74 | systemctl restart wings
75 | fi
76 |
77 | # Octoprint
78 | if [[ -d "/root/OctoPrint" && $OCTOPRINT == true ]]; then
79 | echo -e "\n*** Updating Octoprint ***\n"
80 | # find octoprint
81 | OPRINT=$(find /home -name "oprint")
82 | "$OPRINT"/bin/pip install -U --ignore-installed octoprint
83 | sudo service octoprint restart
84 | fi
85 |
86 | # Docker Compose detection
87 | if [[ -f /usr/local/bin/docker-compose ]]; then DOCKER_COMPOSE_V1=true; fi
88 | if docker compose version &>/dev/null; then DOCKER_COMPOSE_V2=true; fi
89 |
90 | # Docker-Compose run
91 | if [[ $DOCKER_COMPOSE_V1 == true || $DOCKER_COMPOSE_V2 == true ]] && [[ $DOCKER_COMPOSE == true ]]; then
92 | # Cleaning
93 | DOCKER_EXIT () {
94 | echo -e "\n*** Cleaning ***"
95 | docker container prune -f
96 | docker system prune -a -f
97 | docker image prune -f
98 | docker system prune --volumes -f
99 | }
100 | COMPOSEFILES=("docker-compose.yaml" "docker-compose.yml" "compose.yaml" "compose.yml")
101 | DIRLIST=()
102 | for COMPOSEFILE in "${COMPOSEFILES[@]}"; do
103 | while IFS= read -r line; do
104 | DIRLIST+=("$line")
105 | done < <(find "$COMPOSE_PATH" -name "$COMPOSEFILE" -exec dirname {} \; 2> >(grep -v 'Permission denied'))
106 | done
107 | # Docker-Compose v1
108 | if [[ $DOCKER_COMPOSE_V1 == true && ${#DIRLIST[@]} -gt 0 ]]; then
109 | echo -e "\n*** Updating Docker-Compose v1 (oldstable) ***\n"
110 | for dir in "${DIRLIST[@]}"; do
111 | echo "Updating $dir..."
112 | pushd "$dir" > /dev/null || return
113 | /usr/local/bin/docker-compose pull
114 | /usr/local/bin/docker-compose up --force-recreate --build -d
115 | /usr/local/bin/docker-compose restart
116 | popd > /dev/null || return
117 | done
118 | echo "All projects have been updated."
119 | DOCKER_EXIT
120 | fi
121 | # Docker-Compose v2
122 | if [[ $DOCKER_COMPOSE_V2 == true && ${#DIRLIST[@]} -gt 0 ]]; then
123 | echo -e "\n*** Updating Docker Compose ***"
124 | for dir in "${DIRLIST[@]}"; do
125 | echo "Updating $dir..."
126 | pushd "$dir" > /dev/null || return
127 | docker compose pull && docker compose up -d
128 | popd > /dev/null || return
129 | done
130 | echo "All projects have been updated."
131 | DOCKER_EXIT
132 | fi
133 | fi
134 |
135 | # Community / Helper Scripts
136 | if grep -q "community-scripts" /usr/bin/update 2>/dev/null && [[ $INCLUDE_HELPER_SCRIPTS == true ]];then
137 | stdbuf -oL -eL expect <"
143 | send "\r"
144 | expect eof
145 | EOF
146 | echo "✅ Update process completed"
147 | fi
--------------------------------------------------------------------------------
/update.conf:
--------------------------------------------------------------------------------
1 | ╔══════════════════════════════════════════════╗
2 | ║ The Ultimate Updater ║
3 | ║ Config File ║
4 | ╟──────────────────────────────────────────────╢
5 | ║ change 'true/false' or comment out with '#' ║
6 | ╚══════════════════════════════════════════════╝
7 | ┌──────────────────────────────────────────────┐
8 | │ General │
9 | └──────────────────────────────────────────────┘
10 |
11 | VERSION="1.8"
12 | USED_BRANCH="master" # could be "master/beta/develop"
13 |
14 | LOG_FILE="/var/log/ultimate-updater.log"
15 | ERROR_LOG_FILE="/var/log/ultimate-updater-error.log"
16 | VERSION_CHECK="true"
17 | SSH_PORT="22" # VM can be set seperately in VM config file
18 |
19 | # Internet check
20 | # check could be done with "ping" or "curl"
21 | # if use curl - curl must be installed by your self on the machines
22 | # - otherwise check will fail
23 | EXE_FOR_INTERNET_CHECK="ping"
24 | URL_FOR_INTERNET_CHECK="google.com"
25 |
26 |
27 | ╔══════════════════════════════════════════════╗
28 | ║ UPDATER ║
29 | ╚══════════════════════════════════════════════╝
30 | ┌──────────────────────────────────────────────┐
31 | │ Host / LXC / VM │
32 | └──────────────────────────────────────────────┘
33 |
34 | EXIT_ON_ERROR="false" # set to true if you want
35 |
36 | WITH_HOST="true"
37 | WITH_LXC="true"
38 | WITH_VM="true"
39 |
40 | STOPPED_CONTAINER="true"
41 | RUNNING_CONTAINER="true"
42 | STOPPED_VM="true"
43 | RUNNING_VM="true"
44 |
45 | FREEBSD_UPDATES="false" # set to true if you want
46 |
47 | INCLUDE_PHASED_UPDATES="false"
48 | INCLUDE_FSTRIM="false"
49 |
50 | # This line must be set as true or false - comment out will end in true !!!
51 | FSTRIM_WITH_MOUNTPOINT="true"
52 |
53 | # not included for now
54 | INCLUDE_KERNEL="true"
55 | INCLUDE_KERNEL_CLEAN="false"
56 |
57 | LXC_START_DELAY="5" # in secounds
58 | VM_START_DELAY="45" # in secounds - for QEMU - not SSH
59 |
60 | # PACMAN Variable
61 | # for example: "env http_proxy=http://some.proxy:1234"
62 | PACMAN_ENVIRONMENT=""
63 |
64 | INCLUDE_HELPER_SCRIPTS="true"
65 |
66 | ┌──────────────────────────────────────────────┐
67 | │ Only/Exclude LXC and/or VM │
68 | │ Example: ONLY/EXCLUDE="100 110 120" │
69 | │ ! If 'ONLY' is set, 'EXCLUDE' did not work ! │
70 | └──────────────────────────────────────────────┘
71 |
72 | ONLY=""
73 | EXCLUDE=""
74 |
75 | ┌──────────────────────────────────────────────┐
76 | │ Snapshot / Backup │
77 | │ Backup need much more time for the script! │
78 | │ Better make Backups at night with the UI │
79 | └──────────────────────────────────────────────┘
80 |
81 | SNAPSHOT="true"
82 | KEEP_SNAPSHOTS="3"
83 | BACKUP="false"
84 |
85 | ┌──────────────────────────────────────────────┐
86 | │ Extra Updates │
87 | ├──────────────────────────────────────────────┤
88 | │ if GLOBAL is not "true" │
89 | │ Extras will be ignored │
90 | └──────────────────────────────────────────────┘
91 |
92 | EXTRA_GLOBAL="true"
93 | IN_HEADLESS_MODE="false"
94 |
95 | PIHOLE="true"
96 | IOBROKER="true"
97 | PTERODACTYL="true"
98 | OCTOPRINT="true"
99 | DOCKER_COMPOSE="true"
100 | COMPOSE_PATH="/home" # Default is /home but it can be changed to (ex: /root, /opt, ...)
101 |
102 |
103 | ╔══════════════════════════════════════════════╗
104 | ║ Update-Checker for Welcome Screen ║
105 | ╚══════════════════════════════════════════════╝
106 | ┌──────────────────────────────────────────────┐
107 | │ Host / LXC / VM │
108 | └──────────────────────────────────────────────┘
109 |
110 | CHECK_WITH_HOST="true"
111 | CHECK_WITH_LXC="true"
112 | CHECK_WITH_VM="true"
113 |
114 | CHECK_STOPPED_CONTAINER="true"
115 | CHECK_RUNNING_CONTAINER="true"
116 | CHECK_STOPPED_VM="true"
117 | CHECK_RUNNING_VM="true"
118 |
119 | ┌──────────────────────────────────────────────┐
120 | │ Only/Exclude LXC and/or VM │
121 | │ Example: ONLY/EXCLUDE="100 110 120" │
122 | │ ! If 'ONLY' is set, 'EXCLUDE' did not work ! │
123 | └──────────────────────────────────────────────┘
124 |
125 | ONLY_UPDATE_CHECK=""
126 | EXCLUDE_UPDATE_CHECK=""
127 |
--------------------------------------------------------------------------------
/update.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | ##########
4 | # Update #
5 | ##########
6 |
7 | # shellcheck disable=SC1017
8 | # shellcheck disable=SC2034
9 | # shellcheck disable=SC2029
10 | # shellcheck disable=SC2317
11 | # shellcheck disable=SC2320
12 |
13 | VERSION="4.4"
14 |
15 | # Variable / Function
16 | LOCAL_FILES="/etc/ultimate-updater"
17 | CONFIG_FILE="$LOCAL_FILES/update.conf"
18 | USER_SCRIPTS="/etc/ultimate-updater/scripts.d"
19 | BRANCH=$(awk -F'"' '/^USED_BRANCH=/ {print $2}' "$CONFIG_FILE")
20 | SERVER_URL="https://raw.githubusercontent.com/BassT23/Proxmox/$BRANCH"
21 |
22 | # Colors
23 | BL="\e[36m"
24 | OR="\e[1;33m"
25 | RD="\e[1;91m"
26 | GN="\e[1;92m"
27 | CL="\e[0m"
28 |
29 | # Header
30 | HEADER_INFO () {
31 | clear
32 | echo -e "\n \
33 | https://github.com/BassT23/Proxmox\n"
34 | cat <<'EOF'
35 | The __ ______ _ __
36 | / / / / / /_(_)___ ___ ____ _/ /____
37 | / / / / / __/ / __ `__ \/ __ `/ __/ _ \
38 | / /_/ / / /_/ / / / / / / /_/ / /_/ __/
39 | \____/_/\__/_/_/ /_/ /_/\____/\__/\___/
40 | __ __ __ __
41 | / / / /___ ____/ /___ _/ /____ ____
42 | / / / / __ \/ __ / __ `/ __/ _ \/ __/
43 | / /_/ / /_/ / /_/ / /_/ / /_/ __/ /
44 | \____/ ____/\____/\____/\__/\___/_/
45 | /_/ for Proxmox VE
46 | EOF
47 | if [[ "$INFO" != false ]]; then
48 | echo -e "\n \
49 | *** Mode: $MODE***"
50 | if [[ "$HEADLESS" == true ]]; then
51 | echo -e " *** Headless ***"
52 | else
53 | echo -e " *** Interactive ***"
54 | fi
55 | fi
56 | CHECK_ROOT
57 | CHECK_INTERNET
58 | if [[ "$INFO" != false && "$CHECK_VERSION" == true ]]; then VERSION_CHECK; else echo; fi
59 | }
60 |
61 | # Name Changing
62 | NAME_CHANGING () {
63 | if [[ -d /root/Proxmox-Updater/ ]]; then mv /root/Proxmox-Updater/ $LOCAL_FILES/; fi
64 | }
65 |
66 | # Check root
67 | CHECK_ROOT () {
68 | if [[ "$RICM" != true && "$EUID" -ne 0 ]]; then
69 | echo -e "\n${RD} --- Please run this as root ---${CL}\n"
70 | exit 2
71 | fi
72 | }
73 |
74 | # Check internet status
75 | CHECK_INTERNET () {
76 | if ! "$CHECK_URL_EXE" -q -c1 "$CHECK_URL" &>/dev/null; then
77 | echo -e "\n${OR} Internet check fail - Can't update without internet${CL}\n"
78 | exit 2
79 | fi
80 | }
81 |
82 | ARGUMENTS () {
83 | while test $# -gt -0; do
84 | ARGUMENT="$1"
85 | case "$ARGUMENT" in
86 | [0-9][0-9][0-9]|[0-9][0-9][0-9][0-9]|[0-9][0-9][0-9][0-9][0-9])
87 | COMMAND=true
88 | SINGLE_UPDATE=true
89 | MODE=" Single "
90 | ONLY=$ARGUMENT
91 | HEADER_INFO
92 | if [[ $EXIT_ON_ERROR == false ]]; then echo -e "${BL}[Info]${OR} Exit, if error come up, is disabled${CL}\n"; fi
93 | echo -e "${BL}[Info]${OR} Update only LXC/VM $ARGUMENT - work only on main host!${CL}\n"
94 | CONTAINER_UPDATE_START
95 | VM_UPDATE_START
96 | ;;
97 | -h|--help)
98 | USAGE
99 | exit 2
100 | ;;
101 | -s|--silent)
102 | HEADLESS=true
103 | ;;
104 | -v|--version)
105 | VERSION_CHECK
106 | exit 2
107 | ;;
108 | -c)
109 | RICM=true
110 | ;;
111 | -w)
112 | WELCOME_SCREEN=true
113 | ;;
114 | host)
115 | COMMAND=true
116 | if [[ "$RICM" != true ]]; then
117 | MODE=" Host "
118 | HEADER_INFO
119 | if [[ $EXIT_ON_ERROR == false ]]; then echo -e "${BL}[Info]${OR} Exit, if error come up, is disabled${CL}\n"; fi
120 | fi
121 | echo -e "${BL}[Info]${GN} Updating Host${CL} : ${GN}$IP | ($HOSTNAME)${CL}\n"
122 | if [[ "$WITH_HOST" == true ]]; then
123 | UPDATE_HOST_ITSELF
124 | else
125 | echo -e "${BL}[Info] Skipped host itself by the user${CL}\n\n"
126 | fi
127 | if [[ "$WITH_LXC" == true ]]; then
128 | CONTAINER_UPDATE_START
129 | else
130 | echo -e "${BL}[Info] Skipped all containers by the user${CL}\n"
131 | fi
132 | if [[ "$WITH_VM" == true ]]; then
133 | VM_UPDATE_START
134 | else
135 | echo -e "${BL}[Info] Skipped all VMs by the user${CL}\n"
136 | fi
137 | ;;
138 | cluster)
139 | COMMAND=true
140 | MODE="Cluster "
141 | HEADER_INFO
142 | HOST_UPDATE_START
143 | ;;
144 | uninstall)
145 | COMMAND=true
146 | UNINSTALL
147 | exit 2
148 | ;;
149 | master)
150 | if [[ "$2" != -up ]]; then
151 | echo -e "\n${OR} Wrong usage! Use branch update like this:${CL}"
152 | echo -e " update beta -up\n"
153 | exit 2
154 | fi
155 | BRANCH=master
156 | BRANCH_SET=true
157 | ;;
158 | beta)
159 | if [[ "$2" != -up ]]; then
160 | echo -e "\n${OR} Wrong usage! Use branch update like this:${CL}"
161 | echo -e " update beta -up\n"
162 | exit 2
163 | fi
164 | BRANCH=beta
165 | BRANCH_SET=true
166 | ;;
167 | develop)
168 | if [[ "$2" != -up ]]; then
169 | echo -e "\n${OR} Wrong usage! Use branch update like this:${CL}"
170 | echo -e " update beta -up\n"
171 | exit 2
172 | fi
173 | BRANCH=develop
174 | BRANCH_SET=true
175 | ;;
176 | -up)
177 | COMMAND=true
178 | if [[ "$BRANCH_SET" != true ]]; then
179 | BRANCH=master
180 | fi
181 | UPDATE
182 | exit 2
183 | ;;
184 | status)
185 | INFO=false
186 | HEADER_INFO
187 | COMMAND=true
188 | STATUS
189 | exit 2
190 | ;;
191 | *)
192 | echo -e "\n${RD} ❌ Error: Got an unexpected argument \"$ARGUMENT\"${CL}";
193 | USAGE;
194 | exit 2;
195 | ;;
196 | esac
197 | shift
198 | done
199 | }
200 |
201 | # Usage
202 | USAGE () {
203 | if [[ "$HEADLESS" != true ]]; then
204 | echo -e "Usage: $0 [OPTIONS...] {COMMAND}\n"
205 | echo -e "[OPTIONS] Manages the Ultimate Updater:"
206 | echo -e "======================================"
207 | echo -e " -s --silent Silent / Headless Mode"
208 | echo -e " master Use master branch"
209 | echo -e " beta Use beta branch"
210 | echo -e " develop Use develop branch\n"
211 | echo -e "{COMMAND}:"
212 | echo -e "========="
213 | echo -e " -h --help Show help menu"
214 | echo -e " -v --version Show The Ultimate Updater version"
215 | echo -e " -up Update The Ultimate Updater"
216 | echo -e " status Show Status (Version Infos)"
217 | echo -e " uninstall Uninstall The Ultimate Updater\n"
218 | echo -e " host Host-Mode"
219 | echo -e " cluster Cluster-Mode\n"
220 | echo -e "Report issues at: \n"
221 | fi
222 | }
223 |
224 | # Version Check / Update Message in Header
225 | VERSION_CHECK () {
226 | curl -s https://raw.githubusercontent.com/BassT23/Proxmox/master/update.sh > $LOCAL_FILES/temp/update_master.sh
227 | curl -s https://raw.githubusercontent.com/BassT23/Proxmox/beta/update.sh > $LOCAL_FILES/temp/update_beta.sh
228 | curl -s https://raw.githubusercontent.com/BassT23/Proxmox/develop/update.sh > $LOCAL_FILES/temp/update_develop.sh
229 | MASTER_VERSION=$(awk -F'"' '/^VERSION=/ {print $2}' $LOCAL_FILES/temp/update_master.sh)
230 | BETA_VERSION=$(awk -F'"' '/^VERSION=/ {print $2}' $LOCAL_FILES/temp/update_beta.sh)
231 | DEVELOP_VERSION=$(awk -F'"' '/^VERSION=/ {print $2}' $LOCAL_FILES/temp/update_develop.sh)
232 | LOCAL_VERSION=$(awk -F'"' '/^VERSION=/ {print $2}' $LOCAL_FILES/update.sh)
233 | if [[ "$BRANCH" == develop ]]; then
234 | echo -e "${OR}*** The Ultimate Updater is on develop branch ***${CL}"
235 | if [[ "$LOCAL_VERSION" < "$MASTER_VERSION" ]]; then
236 | echo -e "${OR} *** A newer version is available ***${CL}\n\
237 | Installed: $LOCAL_VERSION / Github-Master: $MASTER_VERSION"
238 | if [[ "$HEADLESS" != true ]]; then
239 | echo -e "${OR}Want to update The Ultimate Updater first?${CL}"
240 | read -p "Type [Y/y] or Enter for yes - anything else will skip: " -r
241 | if [[ "$REPLY" =~ ^[Yy]$ || "$REPLY" = "" ]]; then
242 | bash <(curl -s https://raw.githubusercontent.com/BassT23/Proxmox/master/install.sh) update
243 | fi
244 | echo
245 | fi
246 | VERSION_NOT_SHOW=true
247 | elif [[ "$LOCAL_VERSION" < "$BETA_VERSION" ]]; then
248 | echo -e "${OR} *** A newer version is available ***${CL}\n\
249 | Installed: $LOCAL_VERSION / Github-Beta: $BETA_VERSION"
250 | if [[ "$HEADLESS" != true ]]; then
251 | echo -e "${OR}Want to update The Ultimate Updater first?${CL}"
252 | read -p "Type [Y/y] or Enter for yes - anything else will skip: " -r
253 | if [[ "$REPLY" =~ ^[Yy]$ || "$REPLY" = "" ]]; then
254 | bash <(curl -s https://raw.githubusercontent.com/BassT23/Proxmox/beta/install.sh) update
255 | fi
256 | echo
257 | fi
258 | VERSION_NOT_SHOW=true
259 | elif [[ "$LOCAL_VERSION" < "$DEVELOP_VERSION" ]]; then
260 | echo -e "${OR} *** A newer version is available ***${CL}\n\
261 | Installed: $LOCAL_VERSION / Github-Develop: $DEVELOP_VERSION"
262 | if [[ "$HEADLESS" != true ]]; then
263 | echo -e "${OR}Want to update The Ultimate Updater first?${CL}"
264 | read -p "Type [Y/y] or Enter for yes - anything else will skip: " -r
265 | if [[ "$REPLY" =~ ^[Yy]$ || "$REPLY" = "" ]]; then
266 | bash <(curl -s https://raw.githubusercontent.com/BassT23/Proxmox/develop/install.sh) update
267 | fi
268 | echo
269 | fi
270 | VERSION_NOT_SHOW=true
271 | else
272 | echo -e "${GN} The Ultimate Updater is UpToDate${CL}"
273 | fi
274 | fi
275 | if [[ "$BRANCH" == beta ]]; then
276 | echo -e "${OR}*** The Ultimate Updater is on beta branch ***${CL}"
277 | if [[ "$LOCAL_VERSION" < "$MASTER_VERSION" ]]; then
278 | echo -e "${OR} *** A newer version is available ***${CL}\n\
279 | Installed: $LOCAL_VERSION / Github-Master: $MASTER_VERSION"
280 | if [[ "$HEADLESS" != true ]]; then
281 | echo -e "${OR}Want to update The Ultimate Updater first?${CL}"
282 | read -p "Type [Y/y] or Enter for yes - anything else will skip: " -r
283 | if [[ "$REPLY" =~ ^[Yy]$ || "$REPLY" = "" ]]; then
284 | bash <(curl -s https://raw.githubusercontent.com/BassT23/Proxmox/master/install.sh) update
285 | fi
286 | echo
287 | fi
288 | VERSION_NOT_SHOW=true
289 | elif [[ "$LOCAL_VERSION" < "$BETA_VERSION" ]]; then
290 | echo -e "${OR} *** A newer version is available ***${CL}\n\
291 | Installed: $LOCAL_VERSION / Github-Beta: $BETA_VERSION"
292 | if [[ "$HEADLESS" != true ]]; then
293 | echo -e "${OR}Want to update The Ultimate Updater first?${CL}"
294 | read -p "Type [Y/y] or Enter for yes - anything else will skip: " -r
295 | if [[ "$REPLY" =~ ^[Yy]$ || "$REPLY" = "" ]]; then
296 | bash <(curl -s "$SERVER_URL"/install.sh) update
297 | fi
298 | echo
299 | fi
300 | VERSION_NOT_SHOW=true
301 | else
302 | echo -e "\n ${GN}Script is UpToDate${CL}"
303 | fi
304 | fi
305 | if [[ "$BRANCH" == master ]]; then
306 | if [[ "$LOCAL_VERSION" < "$MASTER_VERSION" ]]; then
307 | echo -e "${OR} *** A newer version is available ***${CL}\n\
308 | Installed: $LOCAL_VERSION / Server: $MASTER_VERSION"
309 | if [[ "$HEADLESS" != true ]]; then
310 | echo -e "${OR}Want to update The Ultimate Updater first?${CL}"
311 | read -p "Type [Y/y] or Enter for yes - anything else will skip: " -r
312 | if [[ "$REPLY" =~ ^[Yy]$ || "$REPLY" = "" ]]; then
313 | bash <(curl -s "$SERVER_URL"/install.sh) update
314 | fi
315 | echo
316 | fi
317 | VERSION_NOT_SHOW=true
318 | else
319 | echo -e "\n ${GN}Script is UpToDate${CL}"
320 | fi
321 | fi
322 | if [[ "$VERSION_NOT_SHOW" != true ]]; then echo -e " Version: $VERSION"; fi
323 | rm -rf $LOCAL_FILES/temp/update_master.sh
324 | rm -rf $LOCAL_FILES/temp/update_beta.sh
325 | rm -rf $LOCAL_FILES/temp/update_develop.sh
326 | rm -rf $LOCAL_FILES/temp/update.sh && echo
327 | }
328 |
329 |
330 | # Update The Ultimate Updater
331 | UPDATE () {
332 | echo -e "Update to $BRANCH branch?"
333 | read -p "Type [Y/y] or [Enter] for yes - anything else will exit: " -r
334 | if [[ $REPLY =~ ^[Yy]$ || $REPLY = "" ]]; then
335 | bash <(curl -s "https://raw.githubusercontent.com/BassT23/Proxmox/$BRANCH"/install.sh) update
336 | else
337 | exit 2
338 | fi
339 | }
340 |
341 | # Uninstall
342 | UNINSTALL () {
343 | echo -e "\n${BL}[Info]${OR} Uninstall The Ultimate Updater${CL}\n"
344 | echo -e "${RD}Really want to remove The Ultimate Updater?${CL}"
345 | read -p "Type [Y/y] for yes - anything else will exit: " -r
346 | if [[ "$REPLY" =~ ^[Yy]$ ]]; then
347 | bash <(curl -s "$SERVER_URL"/install.sh) uninstall
348 | exit 2
349 | else
350 | exit 2
351 | fi
352 | }
353 |
354 | STATUS () {
355 | # Get Server Versions
356 | curl -s https://raw.githubusercontent.com/BassT23/Proxmox/"$BRANCH"/update.sh > $LOCAL_FILES/temp/update.sh
357 | curl -s https://raw.githubusercontent.com/BassT23/Proxmox/"$BRANCH"/update-extras.sh > $LOCAL_FILES/temp/update-extras.sh
358 | curl -s https://raw.githubusercontent.com/BassT23/Proxmox/"$BRANCH"/update.conf > $LOCAL_FILES/temp/update.conf
359 | SERVER_VERSION=$(awk -F'"' '/^VERSION=/ {print $2}' $LOCAL_FILES/temp/update.sh)
360 | SERVER_EXTRA_VERSION=$(awk -F'"' '/^VERSION=/ {print $2}' $LOCAL_FILES/temp/update-extras.sh)
361 | SERVER_CONFIG_VERSION=$(awk -F'"' '/^VERSION=/ {print $2}' $LOCAL_FILES/temp/update.conf)
362 | EXTRA_VERSION=$(awk -F'"' '/^VERSION=/ {print $2}' $LOCAL_FILES/update-extras.sh)
363 | CONFIG_VERSION=$(awk -F'"' '/^VERSION=/ {print $2}' $LOCAL_FILES/update.conf)
364 | if [[ "$WELCOME_SCREEN" == true ]]; then
365 | curl -s https://raw.githubusercontent.com/BassT23/Proxmox/"$BRANCH"/welcome-screen.sh > $LOCAL_FILES/temp/welcome-screen.sh
366 | curl -s https://raw.githubusercontent.com/BassT23/Proxmox/"$BRANCH"/check-updates.sh > $LOCAL_FILES/temp/check-updates.sh
367 | SERVER_WELCOME_VERSION=$(awk -F'"' '/^VERSION=/ {print $2}' $LOCAL_FILES/temp/welcome-screen.sh)
368 | SERVER_CHECK_UPDATE_VERSION=$(awk -F'"' '/^VERSION=/ {print $2}' $LOCAL_FILES/temp/check-updates.sh)
369 | WELCOME_VERSION=$(awk -F'"' '/^VERSION=/ {print $2}' /etc/update-motd.d/01-welcome-screen)
370 | CHECK_UPDATE_VERSION=$(awk -F'"' '/^VERSION=/ {print $2}' $LOCAL_FILES/check-updates.sh)
371 | fi
372 | MODIFICATION=$(curl -s https://api.github.com/repos/BassT23/Proxmox | grep pushed_at | cut -d: -f2- | cut -c 3- | rev | cut -c 3- | rev)
373 | echo -e "Last modification (on GitHub): $MODIFICATION\n"
374 | if [[ "$BRANCH" == master ]]; then echo -e "${OR} Version overview${CL}"; else
375 | echo -e "${OR} Version overview ($BRANCH)${CL}"
376 | fi
377 | if [[ "$SERVER_VERSION" != "$VERSION" ]] || [[ "$SERVER_EXTRA_VERSION" != "$EXTRA_VERSION" ]] || [[ "$SERVER_CONFIG_VERSION" != "$CONFIG_VERSION" ]] || [[ "$SERVER_WELCOME_VERSION" != "$WELCOME_VERSION" ]] || [[ "$SERVER_CHECK_UPDATE_VERSION" != "$CHECK_UPDATE_VERSION" ]]; then
378 | echo -e " Local / Server\n"
379 | fi
380 | if [[ "$SERVER_VERSION" == "$VERSION" ]]; then
381 | echo -e " Updater: ${GN}$VERSION${CL}"
382 | else
383 | echo -e " Updater: $VERSION / ${OR}$SERVER_VERSION${CL}"
384 | fi
385 | if [[ "$SERVER_EXTRA_VERSION" == "$EXTRA_VERSION" ]]; then
386 | echo -e " Extras: ${GN}$EXTRA_VERSION${CL}"
387 | else
388 | echo -e " Extras: $EXTRA_VERSION / ${OR}$SERVER_EXTRA_VERSION${CL}"
389 | fi
390 | if [[ "$SERVER_CONFIG_VERSION" == "$CONFIG_VERSION" ]]; then
391 | echo -e " Config: ${GN}$CONFIG_VERSION${CL}"
392 | else
393 | echo -e " Config: $CONFIG_VERSION / ${OR}$SERVER_CONFIG_VERSION${CL}"
394 | fi
395 | if [[ "$WELCOME_SCREEN" == true ]]; then
396 | if [[ "$SERVER_WELCOME_VERSION" == "$WELCOME_VERSION" ]]; then
397 | echo -e " Welcome: ${GN}$WELCOME_VERSION${CL}"
398 | else
399 | echo -e " Welcome: $WELCOME_VERSION / ${OR}$SERVER_WELCOME_VERSION${CL}"
400 | fi
401 | if [[ "$SERVER_CHECK_UPDATE_VERSION" == "$CHECK_UPDATE_VERSION" ]]; then
402 | echo -e " Check: ${GN}$CHECK_UPDATE_VERSION${CL}"
403 | else
404 | echo -e " Check: $CHECK_UPDATE_VERSION / ${OR}$SERVER_CHECK_UPDATE_VERSION${CL}"
405 | fi
406 | fi
407 | echo
408 | rm -r $LOCAL_FILES/temp/*.*
409 | }
410 |
411 | # Read Config File
412 | READ_CONFIG () {
413 | LOG_FILE=$(awk -F'"' '/^LOG_FILE=/ {print $2}' "$CONFIG_FILE")
414 | ERROR_LOG_FILE=$(awk -F'"' '/^ERROR_LOG_FILE=/ {print $2}' "$CONFIG_FILE")
415 | CHECK_VERSION=$(awk -F'"' '/^VERSION_CHECK=/ {print $2}' "$CONFIG_FILE")
416 | CHECK_URL=$(awk -F'"' '/^URL_FOR_INTERNET_CHECK=/ {print $2}' "$CONFIG_FILE")
417 | CHECK_URL_EXE=$(awk -F'"' '/^EXE_FOR_INTERNET_CHECK=/ {print $2}' "$CONFIG_FILE")
418 | if [[ "$CHECK_URL_EXE" == '' ]]; then CHECK_URL_EXE="ping"; fi
419 | SSH_PORT=$(awk -F'"' '/^SSH_PORT=/ {print $2}' "$CONFIG_FILE")
420 | EXIT_ON_ERROR=$(awk -F'"' '/^EXIT_ON_ERROR=/ {print $2}' "$CONFIG_FILE")
421 | WITH_HOST=$(awk -F'"' '/^WITH_HOST=/ {print $2}' "$CONFIG_FILE")
422 | WITH_LXC=$(awk -F'"' '/^WITH_LXC=/ {print $2}' "$CONFIG_FILE")
423 | WITH_VM=$(awk -F'"' '/^WITH_VM=/ {print $2}' "$CONFIG_FILE")
424 | RUNNING_CONTAINER=$(awk -F'"' '/^RUNNING_CONTAINER=/ {print $2}' "$CONFIG_FILE")
425 | STOPPED_CONTAINER=$(awk -F'"' '/^STOPPED_CONTAINER=/ {print $2}' "$CONFIG_FILE")
426 | RUNNING_VM=$(awk -F'"' '/^RUNNING_VM=/ {print $2}' "$CONFIG_FILE")
427 | STOPPED_VM=$(awk -F'"' '/^STOPPED_VM=/ {print $2}' "$CONFIG_FILE")
428 | FREEBSD_UPDATES=$(awk -F'"' '/^FREEBSD_UPDATES=/ {print $2}' "$CONFIG_FILE")
429 | SNAPSHOT=$(awk -F'"' '/^SNAPSHOT/ {print $2}' "$CONFIG_FILE")
430 | KEEP_SNAPSHOT=$(awk -F'"' '/^KEEP_SNAPSHOT/ {print $2}' "$CONFIG_FILE")
431 | BACKUP=$(awk -F'"' '/^BACKUP=/ {print $2}' "$CONFIG_FILE")
432 | LXC_START_DELAY=$(awk -F'"' '/^LXC_START_DELAY=/ {print $2}' "$CONFIG_FILE")
433 | VM_START_DELAY=$(awk -F'"' '/^VM_START_DELAY=/ {print $2}' "$CONFIG_FILE")
434 | EXTRA_GLOBAL=$(awk -F'"' '/^EXTRA_GLOBAL=/ {print $2}' "$CONFIG_FILE")
435 | EXTRA_IN_HEADLESS=$(awk -F'"' '/^IN_HEADLESS_MODE=/ {print $2}' "$CONFIG_FILE")
436 | EXCLUDED=$(awk -F'"' '/^EXCLUDE=/ {print $2}' "$CONFIG_FILE")
437 | ONLY=$(awk -F'"' '/^ONLY=/ {print $2}' "$CONFIG_FILE")
438 | INCLUDE_PHASED_UPDATES=$(awk -F'"' '/^INCLUDE_PHASED_UPDATES=/ {print $2}' "$CONFIG_FILE")
439 | INCLUDE_FSTRIM=$(awk -F'"' '/^INCLUDE_FSTRIM=/ {print $2}' "$CONFIG_FILE")
440 | FSTRIM_WITH_MOUNTPOINT=$(awk -F'"' '/^FSTRIM_WITH_MOUNTPOINT=/ {print $2}' "$CONFIG_FILE")
441 | PACMAN_ENVIRONMENT=$(awk -F'"' '/^PACMAN_ENVIRONMENT=/ {print $2}' "$CONFIG_FILE")
442 | INCLUDE_KERNEL=$(awk -F'"' '/^INCLUDE_KERNEL=/ {print $2}' "$CONFIG_FILE")
443 | INCLUDE_KERNEL_CLEAN=$(awk -F'"' '/^INCLUDE_KERNEL_CLEAN=/ {print $2}' "$CONFIG_FILE")
444 | }
445 |
446 | # ID range support
447 | # need testing
448 | ID_CONVERT () {
449 | expand_ranges() {
450 | local IFS=,
451 | set -- $1
452 | for range; do
453 | case $range in
454 | *-*) for (( i=${range%-*}; i<=${range#*-}; i++ )); do echo $i; done ;;
455 | *) echo $range ;;
456 | esac
457 | done
458 | }
459 | numbers=( $(expand_ranges 11-14,17,20) )
460 | }
461 |
462 | # Snapshot/Backup
463 | CONTAINER_BACKUP () {
464 | if [[ "$SNAPSHOT" == true ]] || [[ "$BACKUP" == true ]]; then
465 | if [[ "$SNAPSHOT" == true ]]; then
466 | if pct snapshot "$CONTAINER" "Update_$(date '+%Y%m%d_%H%M%S')" &>/dev/null; then
467 | echo -e "${BL}[Info]${GN} ✅ Snapshot created${CL}"
468 | echo -e "${BL}[Info]${GN} Delete old snapshots${CL}"
469 | LIST=$(pct listsnapshot "$CONTAINER" | sed -n "s/^.*Update\s*\(\S*\).*$/\1/p" | head -n -"$KEEP_SNAPSHOT")
470 | for SNAPSHOTS in $LIST; do
471 | pct delsnapshot "$CONTAINER" Update"$SNAPSHOTS" >/dev/null 2>&1
472 | done
473 | echo -e "${BL}[Info]${GN} Done${CL}"
474 | else
475 | echo -e "${BL}[Info]${RD} ❌ Snapshot is not possible on your storage${CL}"
476 | fi
477 | fi
478 | if [[ "$BACKUP" == true ]]; then
479 | echo -e "${BL}[Info] Create a backup for LXC (this will take some time - please wait)${CL}"
480 | vzdump "$CONTAINER" --mode stop --storage "$(pvesm status -content backup | grep -m 1 -v ^Name | cut -d ' ' -f1)" --compress zstd
481 | echo -e "${BL}[Info]${GN} ✅ Backup created${CL}\n"
482 | fi
483 | else
484 | echo -e "${BL}[Info]${OR} Snapshot and Backup skipped by the user${CL}"
485 | fi
486 | }
487 | VM_BACKUP () {
488 | if [[ "$SNAPSHOT" == true ]] || [[ "$BACKUP" == true ]]; then
489 | if [[ "$SNAPSHOT" == true ]]; then
490 | if qm snapshot "$VM" "Update_$(date '+%Y%m%d_%H%M%S')" &>/dev/null; then
491 | echo -e "${BL}[Info]${GN} ✅ Snapshot created${CL}"
492 | echo -e "${BL}[Info]${GN} Delete old snapshot(s)${CL}"
493 | LIST=$(qm listsnapshot "$VM" | sed -n "s/^.*Update\s*\(\S*\).*$/\1/p" | head -n -"$KEEP_SNAPSHOT")
494 | for SNAPSHOTS in $LIST; do
495 | qm delsnapshot "$VM" Update"$SNAPSHOTS" >/dev/null 2>&1
496 | done
497 | echo -e "${BL}[Info]${GN} Done${CL}"
498 | else
499 | echo -e "${BL}[Info]${RD} ❌ Snapshot is not possible on your storage${CL}"
500 | fi
501 | fi
502 | if [[ "$BACKUP" == true ]]; then
503 | echo -e "${BL}[Info] Create a backup for the VM (this will take some time - please wait)${CL}"
504 | vzdump "$VM" --mode stop --storage "$(pvesm status -content backup | grep -m 1 -v ^Name | cut -d ' ' -f1)" --compress zstd
505 | echo -e "${BL}[Info]${GN} ✅ Backup created${CL}"
506 | fi
507 | else
508 | echo -e "${BL}[Info]${OR} Snapshot and/or Backup skipped by the user${CL}"
509 | fi
510 | }
511 |
512 | # Extras / User scripts
513 | USER_SCRIPTS () {
514 | if [[ -d $USER_SCRIPTS/$CONTAINER ]]; then
515 | echo -e "\n*** Run user scripts now ***\n"
516 | USER_SCRIPTS_LS=$(ls $USER_SCRIPTS/$CONTAINER)
517 | pct exec "$CONTAINER" -- bash -c "mkdir -p $LOCAL_FILES/user-scripts"
518 | for SCRIPT in $USER_SCRIPTS_LS; do
519 | pct push "$CONTAINER" -- $USER_SCRIPTS/$CONTAINER/$SCRIPT $LOCAL_FILES/user-scripts/$SCRIPT
520 | pct exec "$CONTAINER" -- bash -c "chmod +x $LOCAL_FILES/user-scripts/$SCRIPT && \
521 | $LOCAL_FILES/user-scripts/$SCRIPT"
522 | done
523 | pct exec "$CONTAINER" -- bash -c "rm -rf $LOCAL_FILES || true"
524 | echo -e "\n*** User scripts finished ***\n"
525 | else
526 | echo -e "\n*** Script now can run user scripts also ***\n\
527 | Infos here: \n"
528 | fi
529 | }
530 | USER_SCRIPTS_VM () {
531 | if [[ -d $USER_SCRIPTS/$VM ]]; then
532 | echo -e "\n*** Run user scripts now ***\n"
533 | USER_SCRIPTS_LS=$(ls $USER_SCRIPTS/$VM)
534 | ssh -q -p "$SSH_VM_PORT" -tt "$USER"@"$IP" mkdir -p $LOCAL_FILES/user-scripts/
535 | for SCRIPT in $USER_SCRIPTS_LS; do
536 | scp $USER_SCRIPTS/$CONTAINER/$SCRIPT "$IP":$LOCAL_FILES/user-scripts/$SCRIPT
537 | ssh -q -p "$SSH_VM_PORT" -tt "$USER"@"$IP" "chmod +x $LOCAL_FILES/user-scripts/$SCRIPT && \
538 | $LOCAL_FILES/user-scripts/$SCRIPT"
539 | done
540 | ssh -q -p "$SSH_VM_PORT" -tt "$USER"@"$IP" "rm -rf $LOCAL_FILES || true"
541 | echo -e "\n*** User scripts finished ***\n"
542 | else
543 | echo -e "\n*** Script now can run user scripts also ***\n\
544 | Infos here: \n"
545 | fi
546 | }
547 | EXTRAS () {
548 | if [[ "$EXTRA_GLOBAL" != true ]]; then
549 | echo -e "\n${OR}--- Skip Extra Updates because of the user settings ---${CL}\n"
550 | elif [[ "$HEADLESS" == true && "$EXTRA_IN_HEADLESS" == false ]]; then
551 | echo -e "\n${OR}--- Skip Extra Updates because of Headless Mode or user settings ---${CL}\n"
552 | else
553 | echo -e "\n${OR}--- Searching for extra updates ---${CL}"
554 | if [[ "$SSH_CONNECTION" != true ]]; then
555 | pct exec "$CONTAINER" -- bash -c "mkdir -p $LOCAL_FILES/"
556 | pct push "$CONTAINER" -- $LOCAL_FILES/update-extras.sh $LOCAL_FILES/update-extras.sh
557 | pct push "$CONTAINER" -- $LOCAL_FILES/update.conf $LOCAL_FILES/update.conf
558 | pct exec "$CONTAINER" -- bash -c "chmod +x $LOCAL_FILES/update-extras.sh && \
559 | $LOCAL_FILES/update-extras.sh && \
560 | rm -rf $LOCAL_FILES || true"
561 | USER_SCRIPTS
562 | # Extras in VMS with SSH_CONNECTION
563 | elif [[ "$USER" != root ]]; then
564 | echo -e "${RD}--- You need root user for extra updates - maybe in later relaeses possible ---${CL}"
565 | else
566 | ssh -q -p "$SSH_VM_PORT" -tt "$USER"@"$IP" mkdir -p $LOCAL_FILES/
567 | scp $LOCAL_FILES/update-extras.sh "$IP":$LOCAL_FILES/update-extras.sh
568 | scp $LOCAL_FILES/update.conf "$IP":$LOCAL_FILES/update.conf
569 | ssh -q -p "$SSH_VM_PORT" -tt "$USER"@"$IP" "chmod +x $LOCAL_FILES/update-extras.sh && \
570 | $LOCAL_FILES/update-extras.sh && \
571 | rm -rf $LOCAL_FILES || true"
572 | USER_SCRIPTS_VM
573 | fi
574 | echo -e "${GN}--- Finished extra updates ---${CL}"
575 | if [[ "$WILL_STOP" != true ]] && [[ "$WELCOME_SCREEN" != true ]]; then
576 | echo
577 | elif [[ "$WELCOME_SCREEN" == true ]]; then
578 | echo
579 | fi
580 | fi
581 | }
582 |
583 | # Trim Filesystem
584 | TRIM_FILESYSTEM () {
585 | if [[ "$INCLUDE_FSTRIM" == true ]]; then
586 | ROOT_FS=$(df -Th "/" | awk 'NR==2 {print $2}')
587 | if [[ $(lvs | awk -F '[[:space:]]+' 'NR>1 && (/Data%|'"vm-$CONTAINER"'/) {gsub(/%/, "", $7); print $7}') ]]; then
588 | if [ "$ROOT_FS" = "ext4" ]; then
589 | echo -e "${OR}--- Trimming filesystem ---${CL}"
590 | local BEFORE_TRIM=$(lvs | awk -F '[[:space:]]+' 'NR>1 && (/Data%|'"vm-$CONTAINER"'/) {gsub(/%/, "", $7); print $7}')
591 | echo -e "${RD}Data before trim $BEFORE_TRIM%${CL}"
592 | pct fstrim $CONTAINER --ignore-mountpoints "$FSTRIM_WITH_MOUNTPOINT"
593 | local AFTER_TRIM=$(lvs | awk -F '[[:space:]]+' 'NR>1 && (/Data%|'"vm-$CONTAINER"'/) {gsub(/%/, "", $7); print $7}')
594 | echo -e "${GN}Data after trim $AFTER_TRIM%${CL}\n"
595 | sleep 1.5
596 | fi
597 | fi
598 | fi
599 | }
600 |
601 | # Check Updates for Welcome-Screen
602 | UPDATE_CHECK () {
603 | if [[ "$WELCOME_SCREEN" == true ]]; then
604 | echo -e "${OR}--- Check Status for Welcome-Screen ---${CL}"
605 | if [[ "$CHOST" == true ]]; then
606 | ssh -q -p "$SSH_PORT" "$HOSTNAME" $LOCAL_FILES/check-updates.sh -u chost | tee -a $LOCAL_FILES/check-output
607 | elif [[ "$CCONTAINER" == true ]]; then
608 | ssh -q -p "$SSH_PORT" "$HOSTNAME" $LOCAL_FILES/check-updates.sh -u ccontainer | tee -a $LOCAL_FILES/check-output
609 | elif [[ "$CVM" == true ]]; then
610 | ssh -q -p "$SSH_PORT" "$HOSTNAME" $LOCAL_FILES/check-updates.sh -u cvm | tee -a $LOCAL_FILES/check-output
611 | fi
612 | echo -e "${GN}--- Finished check ---${CL}\n"
613 | if [[ "$WILL_STOP" != true ]]; then echo; fi
614 | else
615 | echo
616 | fi
617 | }
618 |
619 | ## HOST ##
620 | # Host Update Start
621 | HOST_UPDATE_START () {
622 | if [[ "$RICM" != true ]]; then true > $LOCAL_FILES/check-output; fi
623 | for HOST in $HOSTS; do
624 | # Check if Host/Node is available
625 | if ssh -q -p "$SSH_PORT" "$HOST" test >/dev/null 2>&1; [ $? -eq 255 ]; then
626 | echo -e "${BL}[Info] ${OR}Skip Host${CL} : ${GN}$HOST${CL} ${OR}- can't connect${CL}\n"
627 | else
628 | UPDATE_HOST "$HOST"
629 | fi
630 | done
631 | }
632 |
633 | # Host Update
634 | UPDATE_HOST () {
635 | HOST=$1
636 | START_HOST=$(hostname -i | cut -d ' ' -f1)
637 | if [[ "$HOST" != "$START_HOST" ]]; then
638 | ssh -q -p "$SSH_PORT" "$HOST" mkdir -p $LOCAL_FILES/temp
639 | scp "$0" "$HOST":$LOCAL_FILES/update
640 | scp $LOCAL_FILES/update-extras.sh "$HOST":$LOCAL_FILES/update-extras.sh
641 | scp $LOCAL_FILES/update.conf "$HOST":$LOCAL_FILES/update.conf
642 | if [[ "$WELCOME_SCREEN" == true ]]; then
643 | scp $LOCAL_FILES/check-updates.sh "$HOST":$LOCAL_FILES/check-updates.sh
644 | if [[ "$WELCOME_SCREEN" == true ]]; then
645 | scp $LOCAL_FILES/check-output "$HOST":$LOCAL_FILES/check-output
646 | fi
647 | fi
648 | scp /etc/ultimate-updater/temp/exec_host "$HOST":/etc/ultimate-updater/temp
649 | scp -r $LOCAL_FILES/VMs/ "$HOST":$LOCAL_FILES/
650 | fi
651 | if [[ "$HEADLESS" == true ]]; then
652 | ssh -q -p "$SSH_PORT" "$HOST" 'bash -s' < "$0" -- "-s -c host"
653 | elif [[ "$WELCOME_SCREEN" == true ]]; then
654 | ssh -q -p "$SSH_PORT" "$HOST" 'bash -s' < "$0" -- "-c -w host"
655 | else
656 | ssh -q -p "$SSH_PORT" "$HOST" 'bash -s' < "$0" -- "-c host"
657 | fi
658 | }
659 |
660 | UPDATE_HOST_ITSELF () {
661 | echo -e "${OR}--- PVE UPDATE ---${CL}" && pveupdate
662 | if [[ "$HEADLESS" == true ]]; then
663 | echo -e "\n${OR}--- APT UPGRADE HEADLESS ---${CL}" && \
664 | DEBIAN_FRONTEND=noninteractive apt-get dist-upgrade -y || ERROR_CODE=$? && ID=$CONTAINER && ERROR_MSG=$(DEBIAN_FRONTEND=noninteractive apt-get dist-upgrade -y 2>&1) || ERROR
665 | if [[ $ERROR_CODE != "" ]]; then return; fi
666 | else
667 | if [[ "$INCLUDE_PHASED_UPDATES" != "true" ]]; then
668 | echo -e "\n${OR}--- APT UPGRADE ---${CL}" && \
669 | apt-get dist-upgrade -y || ERROR_CODE=$? && ID=$CONTAINER && ERROR_MSG=$(apt-get dist-upgrade -y 2>&1) || ERROR
670 | if [[ $ERROR_CODE != "" ]]; then return; fi
671 | else
672 | echo -e "\n${OR}--- APT UPGRADE ---${CL}" && \
673 | apt-get -o APT::Get::Always-Include-Phased-Updates=true dist-upgrade -y || ERROR_CODE=$? && ID=$CONTAINER && ERROR_MSG=$(apt-get -o APT::Get::Always-Include-Phased-Updates=true dist-upgrade -y 2>&1) || ERROR
674 | if [[ $ERROR_CODE != "" ]]; then return; fi
675 | fi
676 | fi
677 | echo -e "\n${OR}--- APT CLEANING ---${CL}" && \
678 | apt-get --purge autoremove -y || ERROR_CODE=$? && ID=$CONTAINER && ERROR_MSG=$(apt-get --purge autoremove -y 2>&1) || ERROR
679 | if [[ $ERROR_CODE != "" ]]; then return; fi
680 | echo
681 | CHOST="true"
682 | UPDATE_CHECK
683 | CHOST=""
684 | }
685 |
686 | ## Container ##
687 | # Container Update Start
688 | CONTAINER_UPDATE_START () {
689 | # Get the list of containers
690 | CONTAINERS=$(pct list | tail -n +2 | cut -f1 -d' ')
691 | # Loop through the containers
692 | for CONTAINER in $CONTAINERS; do
693 | ERROR_CODE=""
694 | if [[ "$ONLY" == "" && "$EXCLUDED" =~ $CONTAINER ]]; then
695 | echo -e "${BL}[Info] Skipped LXC $CONTAINER by the user${CL}\n\n"
696 | elif [[ "$ONLY" != "" ]] && ! [[ "$ONLY" =~ $CONTAINER ]]; then
697 | if [[ "$SINGLE_UPDATE" != true ]]; then echo -e "${BL}[Info] Skipped LXC $CONTAINER by the user${CL}\n\n"; else continue; fi
698 | else
699 | STATUS=$(pct status "$CONTAINER")
700 | if [[ "$STATUS" == "status: stopped" && "$STOPPED_CONTAINER" == true ]]; then
701 | # Start the container
702 | WILL_STOP="true"
703 | echo -e "${BL}[Info]${GN} Starting LXC ${BL}$CONTAINER ${CL}"
704 | pct start "$CONTAINER"
705 | echo -e "${BL}[Info]${GN} Waiting for LXC ${BL}$CONTAINER${CL}${GN} to start ${CL}"
706 | sleep "$LXC_START_DELAY"
707 | UPDATE_CONTAINER "$CONTAINER"
708 | # Stop the container
709 | echo -e "${BL}[Info]${GN} Shutting down LXC ${BL}$CONTAINER ${CL}\n\n"
710 | pct shutdown "$CONTAINER" &
711 | WILL_STOP="false"
712 | elif [[ "$STATUS" == "status: stopped" && "$STOPPED_CONTAINER" != true ]]; then
713 | echo -e "${BL}[Info] Skipped LXC $CONTAINER by the user${CL}\n\n"
714 | elif [[ "$STATUS" == "status: running" && "$RUNNING_CONTAINER" == true ]]; then
715 | UPDATE_CONTAINER "$CONTAINER"
716 | elif [[ "$STATUS" == "status: running" && "$RUNNING_CONTAINER" != true ]]; then
717 | echo -e "${BL}[Info] Skipped LXC $CONTAINER by the user${CL}\n\n"
718 | else
719 | echo -e "${BL}[Info] Can't find status, please report this issue${CL}\n\n"
720 | fi
721 | fi
722 | done
723 | rm -rf /etc/ultimate-updater/temp/temp
724 | }
725 |
726 | # Container Update
727 | UPDATE_CONTAINER () {
728 | CONTAINER=$1
729 | CCONTAINER="true"
730 | echo 'CONTAINER="'"$CONTAINER"'"' > /etc/ultimate-updater/temp/var
731 | # pct config "$CONTAINER" > /etc/ultimate-updater/temp/temp
732 | OS=$(pct config "$CONTAINER" | awk '/^ostype/' - | cut -d' ' -f2)
733 | NAME=$(pct exec "$CONTAINER" hostname)
734 | # if [[ "$OS" =~ centos ]]; then
735 | # NAME=$(pct exec "$CONTAINER" hostnamectl | grep 'hostname' | tail -n +2 | rev |cut -c -11 | rev)
736 | # else
737 | # NAME=$(pct exec "$CONTAINER" hostname)
738 | # fi
739 | echo -e "${BL}[Info]${GN} Updating LXC ${BL}$CONTAINER${CL} : ${GN}$NAME${CL}\n"
740 | # Check Internet connection
741 | if [[ "$OS" != alpine ]]; then
742 | if ! pct exec "$CONTAINER" -- bash -c "$CHECK_URL_EXE -q -c1 $CHECK_URL &>/dev/null"; then
743 | echo -e "${OR} ❌ Internet check fail - skip this container${CL}\n"
744 | return
745 | fi
746 | # elif [[ "$OS" == alpine ]]; then
747 | # if ! pct exec "$CONTAINER" -- ash -c "$CHECK_URL_EXE -q -c1 $CHECK_URL &>/dev/null"; then
748 | # echo -e "${OR} Internet is not reachable - skip the update${CL}\n"
749 | # return
750 | # fi
751 | fi
752 | # Backup
753 | echo -e "${BL}[Info]${OR} Start Snapshot and/or Backup${CL}"
754 | CONTAINER_BACKUP
755 | echo
756 | # Run update
757 | if [[ "$OS" =~ ubuntu ]] || [[ "$OS" =~ debian ]] || [[ "$OS" =~ devuan ]]; then
758 | echo -e "${OR}--- APT UPDATE ---${CL}"
759 | pct exec "$CONTAINER" -- bash -c "apt-get update -y" || ERROR_CODE=$? && ID=$CONTAINER && ERROR_MSG=$(pct exec "$CONTAINER" -- bash -c "apt-get update -y" 2>&1) || ERROR
760 | if [[ $ERROR_CODE != "" ]]; then return; fi
761 | # Check APT in Container
762 | if pct exec "$CONTAINER" -- bash -c "grep -rnw /etc/apt -e unifi >/dev/null 2>&1"; then
763 | UNIFI="true"
764 | fi
765 | # Check END
766 | if [[ "$HEADLESS" == true || "$UNIFI" == true ]]; then
767 | echo -e "\n${OR}--- APT UPGRADE HEADLESS ---${CL}"
768 | pct exec "$CONTAINER" -- bash -c "DEBIAN_FRONTEND=noninteractive apt-get dist-upgrade -y" || ERROR_CODE=$? && ID=$CONTAINER && ERROR_MSG=$(pct exec "$CONTAINER" -- bash -c "DEBIAN_FRONTEND=noninteractive apt-get dist-upgrade -y" 2>&1) || ERROR
769 | UNIFI=""
770 | if [[ $ERROR_CODE != "" ]]; then return; fi
771 | else
772 | echo -e "\n${OR}--- APT UPGRADE ---${CL}"
773 | if [[ "$INCLUDE_PHASED_UPDATES" != "true" ]]; then
774 | pct exec "$CONTAINER" -- bash -c "apt-get dist-upgrade -y" || ERROR_CODE=$? && ID=$CONTAINER && ERROR_MSG=$(pct exec "$CONTAINER" -- bash -c "apt-get dist-upgrade -y" 2>&1) || ERROR
775 | if [[ $ERROR_CODE != "" ]]; then return; fi
776 | else
777 | pct exec "$CONTAINER" -- bash -c "apt-get -o APT::Get::Always-Include-Phased-Updates=true dist-upgrade -y" || ERROR_CODE=$? && ID=$CONTAINER && ERROR_MSG=$(pct exec "$CONTAINER" -- bash -c "apt-get -o APT::Get::Always-Include-Phased-Updates=true dist-upgrade -y" 2>&1) || ERROR
778 | if [[ $ERROR_CODE != "" ]]; then return; fi
779 | fi
780 | fi
781 | echo -e "\n${OR}--- APT CLEANING ---${CL}"
782 | pct exec "$CONTAINER" -- bash -c "apt-get --purge autoremove -y && apt-get autoclean -y" || ERROR_CODE=$? && ID=$CONTAINER && ERROR_MSG=$(pct exec "$CONTAINER" -- bash -c "apt-get --purge autoremove -y && apt-get autoclean -y" 2>&1) || ERROR
783 | if [[ $ERROR_CODE != "" ]]; then return; fi
784 | EXTRAS
785 | TRIM_FILESYSTEM
786 | UPDATE_CHECK
787 | elif [[ "$OS" =~ fedora ]]; then
788 | echo -e "\n${OR}--- DNF UPGRATE ---${CL}"
789 | pct exec "$CONTAINER" -- bash -c "dnf -y upgrade" || ERROR_CODE=$? && ID=$CONTAINER && ERROR_MSG=$(pct exec "$CONTAINER" -- bash -c "dnf -y upgrade" 2>&1) || ERROR
790 | if [[ $ERROR_CODE != "" ]]; then return; fi
791 | echo -e "\n${OR}--- DNF CLEANING ---${CL}"
792 | pct exec "$CONTAINER" -- bash -c "dnf -y autoremove" || ERROR_CODE=$? && ID=$CONTAINER && ERROR_MSG=$(pct exec "$CONTAINER" -- bash -c "dnf -y autoremove" 2>&1) || ERROR
793 | if [[ $ERROR_CODE != "" ]]; then return; fi
794 | EXTRAS
795 | TRIM_FILESYSTEM
796 | UPDATE_CHECK
797 | elif [[ "$OS" =~ archlinux ]]; then
798 | echo -e "${OR}--- PACMAN UPDATE ---${CL}"
799 | pct exec "$CONTAINER" -- bash -c "$PACMAN_ENVIRONMENT pacman -Su --noconfirm" || ERROR_CODE=$? && ID=$CONTAINER && ERROR_MSG=$(pct exec "$CONTAINER" -- bash -c "$PACMAN_ENVIRONMENT pacman -Su --noconfirm" 2>&1) || ERROR
800 | if [[ $ERROR_CODE != "" ]]; then return; fi
801 | EXTRAS
802 | TRIM_FILESYSTEM
803 | UPDATE_CHECK
804 | elif [[ "$OS" =~ alpine ]]; then
805 | echo -e "${OR}--- APK UPDATE ---${CL}"
806 | pct exec "$CONTAINER" -- ash -c "apk -U upgrade" || ERROR_CODE=$? && ID=$CONTAINER && ERROR_MSG=$(pct exec "$CONTAINER" -- ash -c "apk -U upgrade" 2>&1) || ERROR
807 | if [[ $ERROR_CODE != "" ]]; then return; fi
808 | if [[ "$WILL_STOP" != true ]]; then echo; fi
809 | echo
810 | elif [[ "$OS" =~ centos ]]; then
811 | echo -e "${OR}--- YUM UPDATE ---${CL}"
812 | pct exec "$CONTAINER" -- bash -c "yum -y update" || ERROR_CODE=$? && ID=$CONTAINER && ERROR_MSG=$(pct exec "$CONTAINER" -- bash -c "yum -y update" 2>&1) || ERROR
813 | if [[ $ERROR_CODE != "" ]]; then return; fi
814 | EXTRAS
815 | TRIM_FILESYSTEM
816 | UPDATE_CHECK
817 | else
818 | echo -e "${OR}The system could not be idetified.${CL}"
819 | fi
820 | CCONTAINER=""
821 | }
822 |
823 | ## VM ##
824 | # VM Update Start
825 | VM_UPDATE_START () {
826 | # Get the list of VMs
827 | VMS=$(qm list | tail -n +2 | cut -c -10)
828 | # Loop through the VMs
829 | for VM in $VMS; do
830 | PRE_OS=$(qm config "$VM" | grep ostype || true)
831 | if [[ "$ONLY" == "" && "$EXCLUDED" =~ $VM ]]; then
832 | echo -e "${BL}[Info] Skipped VM $VM by the user${CL}\n\n"
833 | elif [[ "$ONLY" != "" ]] && ! [[ "$ONLY" =~ $VM ]]; then
834 | if [[ "$SINGLE_UPDATE" != true ]]; then echo -e "${BL}[Info] Skipped VM $VM by the user${CL}\n\n"; else continue; fi
835 | elif (qm config "$VM" | grep template >/dev/null 2>&1); then
836 | echo -e "${BL}[Info] ${OR}VM $VM is a template - skip update${CL}\n\n"
837 | return
838 | elif [[ "$PRE_OS" =~ w ]]; then
839 | echo -e "${BL}[Info] Skipped VM $VM${CL}\n"
840 | echo -e "${OR} Windows is not supported for now.\n I'm working on it ;)${CL}\n\n"
841 | else
842 | STATUS=$(qm status "$VM")
843 | if [[ "$STATUS" == "status: stopped" && "$STOPPED_VM" == true ]]; then
844 | # Check if update is possible
845 | if [[ $(qm config "$VM" | grep 'agent:' | sed 's/agent:\s*//') == 1 ]] || [[ -f $LOCAL_FILES/VMs/"$VM" ]]; then
846 | # Start the VM
847 | WILL_STOP="true"
848 | echo -e "${BL}[Info]${GN} Starting VM${BL} $VM ${CL}"
849 | qm start "$VM" >/dev/null 2>&1
850 | START_WAITING="true"
851 | UPDATE_VM "$VM"
852 | # Stop the VM
853 | echo -e "${BL}[Info]${GN} Shutting down VM${BL} $VM ${CL}\n\n"
854 | qm shutdown "$VM" &
855 | WILL_STOP="false"
856 | START_WAITING="false"
857 | else
858 | echo -e "${BL}[Info] Skipped VM $VM because, QEMU or SSH hasn't initialized${CL}\n\n"
859 | fi
860 | elif [[ "$STATUS" == "status: stopped" && "$STOPPED_VM" != true ]]; then
861 | echo -e "${BL}[Info] Skipped VM $VM by the user${CL}\n\n"
862 | elif [[ "$STATUS" == "status: running" && "$RUNNING_VM" == true ]]; then
863 | UPDATE_VM "$VM"
864 | elif [[ "$STATUS" == "status: running" && "$RUNNING_VM" != true ]]; then
865 | echo -e "${BL}[Info] Skipped VM $VM by the user${CL}\n\n"
866 | else
867 | echo -e "${BL}[Info] Can't find status, please report this issue${CL}\n\n"
868 | fi
869 | fi
870 | done
871 | }
872 |
873 | # VM Update
874 | UPDATE_VM () {
875 | VM=$1
876 | NAME=$(qm config "$VM" | grep 'name:' | sed 's/name:\s*//')
877 | CVM="true"
878 | echo 'VM="'"$VM"'"' > /etc/ultimate-updater/temp/var
879 | echo -e "${BL}[Info]${GN} Updating VM ${BL}$VM${CL} : ${GN}$NAME${CL}\n"
880 | # Backup
881 | echo -e "${BL}[Info]${OR} Start Snapshot and/or Backup${CL}"
882 | VM_BACKUP
883 | echo
884 | # Read SSH config file - check how update is possible
885 | if [[ -f $LOCAL_FILES/VMs/"$VM" ]]; then
886 | IP=$(awk -F'"' '/^IP=/ {print $2}' $LOCAL_FILES/VMs/"$VM")
887 | USER=$(awk -F'"' '/^USER=/ {print $2}' $LOCAL_FILES/VMs/"$VM")
888 | if [[ -z "$USER" ]]; then USER="root"; fi
889 | SSH_VM_PORT=$(awk -F'"' '/^SSH_VM_PORT=/ {print $2}' $LOCAL_FILES/VMs/"$VM")
890 | if [[ -z "$SSH_VM_PORT" ]]; then SSH_VM_PORT="22"; fi
891 | SSH_START_DELAY_TIME=$(awk -F'"' '/^SSH_START_DELAY_TIME=/ {print $2}' $LOCAL_FILES/VMs/"$VM")
892 | if [[ -z "$SSH_START_DELAY_TIME" ]]; then SSH_START_DELAY_TIME="45"; fi
893 | if [[ "$START_WAITING" == true ]]; then
894 | echo -e "${BL}[Info]${OR} Wait for bootup${CL}"
895 | echo -e "${BL}[Info]${OR} Sleep $SSH_START_DELAY_TIME secounds - time could be set in SSH-VM config file${CL}\n"
896 | sleep "$SSH_START_DELAY_TIME"
897 | fi
898 | if ! (ssh -o BatchMode=yes -o ConnectTimeout=5 -q -p "$SSH_VM_PORT" "$USER"@"$IP" exit >/dev/null 2>&1); then
899 | echo -e "${RD} ❌ File for ssh connection found, but not correctly set?\n\
900 | ${OR}Or need more start delay time.\n\
901 | ${BL}Please check SSH Key-Based Authentication${CL}\n\
902 | Infos can be found here:
903 | Try to use QEMU insead\n"
904 | START_WAITING=false
905 | UPDATE_VM_QEMU
906 | else
907 | # Run SSH Update
908 | SSH_CONNECTION="true"
909 | KERNEL=$(qm guest cmd "$VM" get-osinfo 2>/dev/null | grep kernel-version || true)
910 | OS=$(ssh -q -p "$SSH_VM_PORT" "$USER"@"$IP" hostnamectl 2>/dev/null | grep System || true)
911 | if [[ "$KERNEL" =~ FreeBSD ]] && [[ "$FREEBSD_UPDATES" == true ]]; then
912 | echo -e "${OR}--- PKG UPDATE ---${CL}"
913 | ssh -t -q -p "$SSH_VM_PORT" -tt "$USER"@"$IP" pkg update || ERROR_CODE=$? && ID=$CONTAINER && ERROR_MSG=$(ssh -t -q -p "$SSH_VM_PORT" -tt "$USER"@"$IP" pkg update 2>&1) || ERROR
914 | if [[ $ERROR_CODE != "" ]]; then return; fi
915 | echo -e "\n${OR}--- PKG UPGRADE ---${CL}"
916 | ssh -t -q -p "$SSH_VM_PORT" -tt "$USER"@"$IP" pkg upgrade -y || ERROR_CODE=$? && ID=$CONTAINER && ERROR_MSG=$(ssh -t -q -p "$SSH_VM_PORT" -tt "$USER"@"$IP" pkg upgrade -y 2>&1) || ERROR
917 | if [[ $ERROR_CODE != "" ]]; then return; fi
918 | echo -e "\n${OR}--- PKG CLEANING ---${CL}"
919 | ssh -t -q -p "$SSH_VM_PORT" -tt "$USER"@"$IP" pkg autoremove -y || ERROR_CODE=$? && ID=$CONTAINER && ERROR_MSG=$(ssh -t -q -p "$SSH_VM_PORT" -tt "$USER"@"$IP" pkg autoremove -y 2>&1) || ERROR
920 | if [[ $ERROR_CODE != "" ]]; then return; fi
921 | echo
922 | # UPDATE_CHECK
923 | return
924 | elif [[ "$KERNEL" =~ FreeBSD ]]; then
925 | echo -e "${OR} Free BSD skipped by user${CL}\n"
926 | return
927 | elif [[ "$OS" =~ Ubuntu ]] || [[ "$OS" =~ Debian ]] || [[ "$OS" =~ Devuan ]]; then
928 | # Check Internet connection
929 | if ! ssh -q -p "$SSH_VM_PORT" "$USER"@"$IP" "$CHECK_URL_EXE" -c1 "$CHECK_URL" &>/dev/null; then
930 | echo -e "${OR} ❌ Internet check fail - skip this VM${CL}\n"
931 | return
932 | fi
933 | if [[ "$USER" != root ]]; then
934 | UPDATE_USER="sudo "
935 | fi
936 | echo -e "${OR}--- APT UPDATE ---${CL}"
937 | ssh -q -p "$SSH_VM_PORT" -tt "$USER"@"$IP" "$UPDATE_USER"apt-get update -y || ERROR_CODE=$? && ID=$CONTAINER && ERROR_MSG=$(ssh -q -p "$SSH_VM_PORT" -tt "$USER"@"$IP" "$UPDATE_USER"apt-get update -y 2>&1) || ERROR
938 | if [[ $ERROR_CODE != "" ]]; then return; fi
939 | echo -e "\n${OR}--- APT UPGRADE ---${CL}"
940 | if [[ "$INCLUDE_PHASED_UPDATES" != "true" ]]; then
941 | ssh -t -q -p "$SSH_VM_PORT" -tt "$USER"@"$IP" "$UPDATE_USER" apt-get upgrade -y || ERROR_CODE=$? && ID=$CONTAINER && ERROR_MSG=$(ssh -t -q -p "$SSH_VM_PORT" -tt "$USER"@"$IP" "$UPDATE_USER" apt-get upgrade -y 2>&1) || ERROR
942 | if [[ $ERROR_CODE != "" ]]; then return; fi
943 | else
944 | ssh -q -p "$SSH_VM_PORT" -tt "$USER"@"$IP" "$UPDATE_USER" apt-get -o APT::Get::Always-Include-Phased-Updates=true upgrade -y || ERROR_CODE=$? && ID=$CONTAINER && ERROR_MSG=$(ssh -q -p "$SSH_VM_PORT" -tt "$USER"@"$IP" "$UPDATE_USER" apt-get -o APT::Get::Always-Include-Phased-Updates=true upgrade -y 2>&1) || ERROR
945 | if [[ $ERROR_CODE != "" ]]; then return; fi
946 | fi
947 | echo -e "\n${OR}--- APT CLEANING ---${CL}"
948 | ssh -q -p "$SSH_VM_PORT" -tt "$USER"@"$IP" "$UPDATE_USER" "apt-get --purge autoremove -y && apt-get autoclean -y" || ERROR_CODE=$? && ID=$CONTAINER && ERROR_MSG=$(ssh -q -p "$SSH_VM_PORT" -tt "$USER"@"$IP" "$UPDATE_USER" apt-get --purge autoremove -y 2>&1) || ERROR
949 | if [[ $ERROR_CODE != "" ]]; then return; fi
950 | EXTRAS
951 | UPDATE_CHECK
952 | elif [[ "$OS" =~ Fedora ]]; then
953 | echo -e "\n${OR}--- DNF UPGRADE ---${CL}"
954 | ssh -t -q -p "$SSH_VM_PORT" -tt "$USER"@"$IP" dnf -y upgrade || ERROR_CODE=$? && ID=$CONTAINER && ERROR_MSG=$(ssh -t -q -p "$SSH_VM_PORT" -tt "$USER"@"$IP" dnf -y upgrade 2>&1) || ERROR
955 | if [[ $ERROR_CODE != "" ]]; then return; fi
956 | echo -e "\n${OR}--- DNF CLEANING ---${CL}"
957 | ssh -q -p "$SSH_VM_PORT" "$USER"@"$IP" dnf -y --purge autoremove || ERROR_CODE=$? && ID=$CONTAINER && ERROR_MSG=$(ssh -q -p "$SSH_VM_PORT" "$USER"@"$IP" dnf -y --purge autoremove 2>&1) || ERROR
958 | if [[ $ERROR_CODE != "" ]]; then return; fi
959 | EXTRAS
960 | UPDATE_CHECK
961 | elif [[ "$OS" =~ Arch ]]; then
962 | echo -e "${OR}--- PACMAN UPDATE ---${CL}"
963 | ssh -t -q -p "$SSH_VM_PORT" -tt "$USER"@"$IP" pacman -Su --noconfirm || ERROR_CODE=$? && ID=$CONTAINER && ERROR_MSG=$(ssh -t -q -p "$SSH_VM_PORT" -tt "$USER"@"$IP" pacman -Su --noconfirm 2>&1) || ERROR
964 | if [[ $ERROR_CODE != "" ]]; then return; fi
965 | EXTRAS
966 | UPDATE_CHECK
967 | elif [[ "$OS" =~ Alpine ]]; then
968 | echo -e "${OR}--- APK UPDATE ---${CL}"
969 | ssh -t -q -p "$SSH_VM_PORT" -tt "$USER"@"$IP" apk -U upgrade || ERROR_CODE=$? && ID=$CONTAINER && ERROR_MSG=$(ssh -t -q -p "$SSH_VM_PORT" -tt "$USER"@"$IP" apk -U upgrade 2>&1) || ERROR
970 | if [[ $ERROR_CODE != "" ]]; then return; fi
971 | elif [[ "$OS" =~ CentOS ]]; then
972 | echo -e "${OR}--- YUM UPDATE ---${CL}"
973 | ssh -t -q -p "$SSH_VM_PORT" -tt "$USER"@"$IP" yum -y update || ERROR_CODE=$? && ID=$CONTAINER && ERROR_MSG=$(ssh -t -q -p "$SSH_VM_PORT" -tt "$USER"@"$IP" yum -y update 2>&1) || ERROR
974 | if [[ $ERROR_CODE != "" ]]; then return; fi
975 | EXTRAS
976 | UPDATE_CHECK
977 | else
978 | echo -e "${RD} ❌ The system is not supported.\n Maybe with later version ;)\n${CL}"
979 | echo -e " If you want, make a request here: \n"
980 | fi
981 | return
982 | # elif [[ $OS_BASE == win10 ]]; then
983 | # ssh -q -p "$SSH_PORT" "$USER"@"$IP" wuauclt /detectnow /updatenow
984 | # Install-WindowsUpdate -MicrosoftUpdate -AcceptAll -AutoReboot # don't work
985 | fi
986 | else
987 | UPDATE_VM_QEMU
988 | fi
989 | }
990 |
991 | # QEMU
992 | UPDATE_VM_QEMU () {
993 | echo -e "${BL}[Info]${GN} Try to connect via QEMU${CL}"
994 | if [[ "$START_WAITING" == true ]]; then
995 | echo -e "${BL}[Info]${OR} Wait for bootup${CL}"
996 | echo -e "${BL}[Info]${OR} Sleep $VM_START_DELAY secounds - time could be set in config file${CL}\n"
997 | sleep "$VM_START_DELAY"
998 | fi
999 | if qm guest exec "$VM" test >/dev/null 2>&1; then
1000 | echo -e "${OR} QEMU found. SSH connection is also available - with better output.${CL}\n\
1001 | Please look here: \n"
1002 | # Run Update
1003 | KERNEL=$(qm guest cmd "$VM" get-osinfo | grep kernel-version || true)
1004 | OS=$(qm guest cmd "$VM" get-osinfo | grep name || true)
1005 | if [[ "$KERNEL" =~ FreeBSD ]] && [[ "$FREEBSD_UPDATES" == true ]]; then
1006 | echo -e "${OR}--- PKG UPDATE ---${CL}"
1007 | qm guest exec "$VM" -- tcsh -c "pkg update" | tail -n +4 | head -n -1 | cut -c 17- || ERROR_CODE=$? && ID=$CONTAINER && ERROR_MSG=$(qm guest exec "$VM" -- tcsh -c "pkg update" | tail -n +4 | head -n -1 | cut -c 17- 2>&1) || ERROR
1008 | if [[ $ERROR_CODE != "" ]]; then return; fi
1009 | echo -e "\n${OR}--- PKG UPGRADE ---${CL}"
1010 | qm guest exec "$VM" -- tcsh -c "pkg upgrade -y" | tail -n +2 | head -n -1 || ERROR_CODE=$? && ID=$CONTAINER && ERROR_MSG=$(qm guest exec "$VM" -- tcsh -c "pkg upgrade -y" | tail -n +2 | head -n -1 2>&1) || ERROR
1011 | if [[ $ERROR_CODE != "" ]]; then return; fi
1012 | echo -e "\n${OR}--- PKG CLEANING ---${CL}"
1013 | qm guest exec "$VM" -- tcsh -c "pkg autoremove -y" | tail -n +4 | head -n -1 | cut -c 17- || ERROR_CODE=$? && ID=$CONTAINER && ERROR_MSG=$(qm guest exec "$VM" -- tcsh -c "pkg autoremove -y" | tail -n +4 | head -n -1 | cut -c 17- 2>&1) || ERROR
1014 | if [[ $ERROR_CODE != "" ]]; then return; fi
1015 | echo
1016 | UPDATE_CHECK
1017 | return
1018 | elif [[ "$KERNEL" =~ FreeBSD ]]; then
1019 | echo -e "${OR} Free BSD skipped by user${CL}\n"
1020 | return
1021 | elif [[ "$OS" =~ Ubuntu ]] || [[ "$OS" =~ Debian ]] || [[ "$OS" =~ Devuan ]]; then
1022 | # Check Internet connection
1023 | if ! (qm guest exec "$VM" -- bash -c "$CHECK_URL_EXE -q -c1 $CHECK_URL &>/dev/null"); then
1024 | echo -e "${OR} ❌ Internet is not reachable - skip the update${CL}\n"
1025 | return
1026 | fi
1027 | echo -e "${OR}--- APT UPDATE ---${CL}"
1028 | qm guest exec "$VM" -- bash -c "apt-get update -y" | tail -n +4 | head -n -1 | cut -c 17- || ERROR_CODE=$? && ID=$CONTAINER && ERROR_MSG=$(qm guest exec "$VM" -- bash -c "apt-get update -y" | tail -n +4 | head -n -1 | cut -c 17- 2>&1) || ERROR
1029 | if [[ $ERROR_CODE != "" ]]; then return; fi
1030 | echo -e "\n${OR}--- APT UPGRADE ---${CL}"
1031 | if [[ "$INCLUDE_PHASED_UPDATES" != "true" ]]; then
1032 | qm guest exec "$VM" --timeout 120 -- bash -c "apt-get upgrade -y" | tail -n +2 | head -n -1 || ERROR_CODE=$? && ID=$CONTAINER && ERROR_MSG=$(qm guest exec "$VM" --timeout 120 -- bash -c "apt-get upgrade -y" | tail -n +2 | head -n -1 2>&1) || ERROR
1033 | if [[ $ERROR_CODE != "" ]]; then return; fi
1034 | else
1035 | qm guest exec "$VM" --timeout 120 -- bash -c "apt-get -o APT::Get::Always-Include-Phased-Updates=true upgrade -y" | tail -n +2 | head -n -1 || ERROR_CODE=$? && ID=$CONTAINER && ERROR_MSG=$(qm guest exec "$VM" --timeout 120 -- bash -c "apt-get -o APT::Get::Always-Include-Phased-Updates=true upgrade -y" | tail -n +2 | head -n -1 2>&1) || ERROR
1036 | if [[ $ERROR_CODE != "" ]]; then return; fi
1037 | fi
1038 | echo -e "\n${OR}--- APT CLEANING ---${CL}"
1039 | qm guest exec "$VM" -- bash -c "apt-get --purge autoremove -y && apt-get autoclean -y" | tail -n +4 | head -n -1 | cut -c 17- || ERROR_CODE=$? && ID=$CONTAINER && ERROR_MSG=$(qm guest exec "$VM" -- bash -c "apt-get --purge autoremove -y && apt-get autoclean -y" | tail -n +4 | head -n -1 | cut -c 17- 2>&1) || ERROR
1040 | if [[ $ERROR_CODE != "" ]]; then return; fi
1041 | echo
1042 | UPDATE_CHECK
1043 | elif [[ "$OS" =~ Fedora ]]; then
1044 | echo -e "\n${OR}--- DNF UPGRADE ---${CL}"
1045 | qm guest exec "$VM" -- bash -c "dnf -y upgrade" | tail -n +2 | head -n -1 || ERROR_CODE=$? && ID=$CONTAINER && ERROR_MSG=$(qm guest exec "$VM" -- bash -c "dnf -y upgrade" | tail -n +2 | head -n -1 2>&1) || ERROR
1046 | if [[ $ERROR_CODE != "" ]]; then return; fi
1047 | echo -e "\n${OR}--- DNF CLEANING ---${CL}"
1048 | qm guest exec "$VM" -- bash -c "dnf -y --purge autoremove" | tail -n +4 | head -n -1 | cut -c 17- || ERROR_CODE=$? && ID=$CONTAINER && ERROR_MSG=$(qm guest exec "$VM" -- bash -c "dnf -y --purge autoremove" | tail -n +4 | head -n -1 | cut -c 17- 2>&1) || ERROR
1049 | if [[ $ERROR_CODE != "" ]]; then return; fi
1050 | echo
1051 | UPDATE_CHECK
1052 | elif [[ "$OS" =~ Arch ]]; then
1053 | echo -e "${OR}--- PACMAN UPDATE ---${CL}"
1054 | qm guest exec "$VM" -- bash -c "pacman -Su --noconfirm" | tail -n +2 | head -n -1 || ERROR_CODE=$? && ID=$CONTAINER && ERROR_MSG=$(qm guest exec "$VM" -- bash -c "pacman -Su --noconfirm" | tail -n +2 | head -n -1 2>&1) || ERROR
1055 | if [[ $ERROR_CODE != "" ]]; then return; fi
1056 | echo
1057 | UPDATE_CHECK
1058 | elif [[ "$OS" =~ Alpine ]]; then
1059 | echo -e "${OR}--- APK UPDATE ---${CL}"
1060 | qm guest exec "$VM" -- ash -c "apk -U upgrade" | tail -n +2 | head -n -1 || ERROR_CODE=$? && ID=$CONTAINER && ERROR_MSG=$(qm guest exec "$VM" -- ash -c "apk -U upgrade" | tail -n +2 | head -n -1 2>&1) || ERROR
1061 | if [[ $ERROR_CODE != "" ]]; then return; fi
1062 | elif [[ "$OS" =~ CentOS ]]; then
1063 | echo -e "${OR}--- YUM UPDATE ---${CL}"
1064 | qm guest exec "$VM" -- bash -c "yum -y update" | tail -n +2 | head -n -1 || ERROR_CODE=$? && ID=$CONTAINER && ERROR_MSG=$(qm guest exec "$VM" -- bash -c "yum -y update" | tail -n +2 | head -n -1 2>&1) || ERROR
1065 | if [[ $ERROR_CODE != "" ]]; then return; fi
1066 | echo
1067 | UPDATE_CHECK
1068 | else
1069 | echo -e "${RD} The system is not supported.\n Maybe with later version ;)\n${CL}"
1070 | echo -e " If you want, make a request here: \n"
1071 | fi
1072 | else
1073 | echo -e "${RD} ❌ SSH or QEMU guest agent is not initialized on VM ${CL}\n\
1074 | ${OR}If you want to update VMs, you must set up it by yourself!${CL}\n\
1075 | For ssh (harder, but nicer output), check this: \n\
1076 | For QEMU (easy connection), check this: \n"
1077 | fi
1078 | CVM=""
1079 | }
1080 |
1081 | ## General ##
1082 | READ_CONFIG
1083 |
1084 | # Logging
1085 | OUTPUT_TO_FILE () {
1086 | echo 'EXEC_HOST="'"$HOSTNAME"'"' > /etc/ultimate-updater/temp/exec_host
1087 | if [[ "$RICM" != true ]]; then
1088 | touch "$LOG_FILE"
1089 | exec &> >(tee "$LOG_FILE")
1090 | fi
1091 | # Welcome-Screen
1092 | if [[ -f "/etc/update-motd.d/01-welcome-screen" && -x "/etc/update-motd.d/01-welcome-screen" ]]; then
1093 | WELCOME_SCREEN=true
1094 | if [[ "$RICM" != true ]]; then
1095 | touch $LOCAL_FILES/check-output
1096 | fi
1097 | fi
1098 | }
1099 | CLEAN_LOGFILE () {
1100 | if [[ "$RICM" != true ]]; then
1101 | tail -n +2 "$LOG_FILE" > tmp.log && mv tmp.log "$LOG_FILE"
1102 | cat "$LOG_FILE" | sed -r "s/\x1B\[([0-9]{1,3}(;[0-9]{1,3})*)?[mGK]//g" | tee "$LOG_FILE" >/dev/null 2>&1
1103 | chmod 640 "$LOG_FILE"
1104 | if [[ -f ./tmp.log ]]; then
1105 | rm -rf ./tmp.log
1106 | fi
1107 | fi
1108 | }
1109 |
1110 | # Error handling
1111 | ERROR () {
1112 | echo -e "$ID : $NAME" | tee -a "$ERROR_LOG_FILE" >/dev/null 2>&1
1113 | echo -e "Error code: $ERROR_CODE" | tee -a "$ERROR_LOG_FILE" >/dev/null 2>&1
1114 | echo -e "Error output: $ERROR_MSG\n" | tee -a "$ERROR_LOG_FILE" >/dev/null 2>&1
1115 | echo
1116 | }
1117 | ERROR_LOGGING () {
1118 | touch "$ERROR_LOG_FILE"
1119 | > "$ERROR_LOG_FILE"
1120 | }
1121 | if [[ $EXIT_ON_ERROR == false ]]; then
1122 | ERROR_LOGGING
1123 | else
1124 | set -e
1125 | fi
1126 |
1127 | # Exit
1128 | EXIT () {
1129 | EXIT_CODE=$?
1130 | if [[ -f "/etc/ultimate-updater/temp/exec_host" ]]; then
1131 | EXEC_HOST=$(awk -F'"' '/^EXEC_HOST=/ {print $2}' /etc/ultimate-updater/temp/exec_host)
1132 | else
1133 | echo "no exec host file exist"
1134 | fi
1135 | if [[ "$WELCOME_SCREEN" == true ]]; then
1136 | scp "$LOCAL_FILES"/check-output "$EXEC_HOST":"$LOCAL_FILES"/check-output
1137 | fi
1138 | # Exit without echo
1139 | if [[ "$EXIT_CODE" == 2 ]]; then
1140 | exit
1141 | # Update Finish
1142 | elif [[ "$EXIT_CODE" == 0 ]]; then
1143 | if [[ "$RICM" != true ]]; then
1144 | if [[ -f "$ERROR_LOG_FILE" ]] && [[ -s "$ERROR_LOG_FILE" ]]; then
1145 | echo -e "${OR}❌ Finished, with errors.${CL}\n"
1146 | echo -e "Please checkout $ERROR_LOG_FILE"
1147 | #$LOCAL_FILES/exit/error.sh
1148 | echo
1149 | CLEAN_LOGFILE
1150 | else
1151 | echo -e "${GN}✅ Finished, all updates done.${CL}\n"
1152 | $LOCAL_FILES/exit/passed.sh
1153 | CLEAN_LOGFILE
1154 | fi
1155 | fi
1156 | else
1157 | # Update Error
1158 | if [[ "$RICM" != true ]]; then
1159 | echo -e "${RD}❌ Error during update --- Exit Code: $EXIT_CODE${CL}\n"
1160 | $LOCAL_FILES/exit/error.sh
1161 | CLEAN_LOGFILE
1162 | fi
1163 | fi
1164 | sleep 3
1165 | rm -rf /etc/ultimate-updater/temp/var
1166 | rm -rf $LOCAL_FILES/update
1167 | if [[ -f "/etc/ultimate-updater/temp/exec_host" && "$HOSTNAME" != "$EXEC_HOST" ]]; then rm -rf "$LOCAL_FILES"; fi
1168 | }
1169 | trap EXIT EXIT
1170 |
1171 | # Check Cluster Mode
1172 | if [[ -f "/etc/corosync/corosync.conf" ]]; then
1173 | HOSTS=$(awk '/ring0_addr/{print $2}' "/etc/corosync/corosync.conf")
1174 | MODE="Cluster "
1175 | else
1176 | MODE=" Host "
1177 | fi
1178 |
1179 | # Run
1180 | NAME_CHANGING
1181 | export TERM=xterm-256color
1182 | if ! [[ -d "/etc/ultimate-updater/temp" ]]; then mkdir /etc/ultimate-updater/temp; fi
1183 | OUTPUT_TO_FILE
1184 | IP=$(hostname -i | cut -d ' ' -f1)
1185 | ARGUMENTS "$@"
1186 |
1187 | # Run without commands (Automatic Mode)
1188 | if [[ "$COMMAND" != true ]]; then
1189 | HEADER_INFO
1190 | if [[ $EXIT_ON_ERROR == false ]]; then echo -e "${BL}[Info]${OR} Exit, if error come up, is disabled${CL}\n"; fi
1191 | if [[ "$MODE" =~ Cluster ]]; then
1192 | HOST_UPDATE_START
1193 | else
1194 | echo -e "${BL}[Info]${GN} Updating Host${CL} : ${GN}$IP | ($HOSTNAME)${CL}\n"
1195 | if [[ "$WITH_HOST" == true ]]; then
1196 | UPDATE_HOST_ITSELF
1197 | else
1198 | echo -e "${BL}[Info] Skipped host itself by the user${CL}\n\n"
1199 | fi
1200 | if [[ "$WITH_LXC" == true ]]; then
1201 | CONTAINER_UPDATE_START
1202 | else
1203 | echo -e "${BL}[Info] Skipped all containers by the user${CL}\n"
1204 | fi
1205 | if [[ "$WITH_VM" == true ]]; then
1206 | VM_UPDATE_START
1207 | else
1208 | echo -e "${BL}[Info] Skipped all VMs by the user${CL}\n"
1209 | fi
1210 | fi
1211 | fi
1212 |
1213 | exit 0
1214 |
--------------------------------------------------------------------------------
/welcome-screen.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | ##################
4 | # Welcome-Screen #
5 | ##################
6 |
7 | # shellcheck disable=SC1017
8 | # shellcheck disable=SC2034
9 |
10 | VERSION="1.6"
11 |
12 | # Variable / Function
13 | LOCAL_FILES="/etc/ultimate-updater"
14 | CONFIG_FILE="$LOCAL_FILES/update.conf"
15 | BRANCH=$(awk -F'"' '/^USED_BRANCH=/ {print $2}' "$CONFIG_FILE")
16 | CHECK_OUTPUT=$(stat -c%s $LOCAL_FILES/check-output)
17 | SERVER_URL="https://raw.githubusercontent.com/BassT23/Proxmox/$BRANCH"
18 |
19 | # Colors
20 | OR="\e[1;33m"
21 | GN="\e[1;92m"
22 | CL="\e[0m"
23 |
24 | # Version Check
25 | VERSION_CHECK () {
26 | curl -s https://raw.githubusercontent.com/BassT23/Proxmox/master/update.sh > /root/update_master.sh
27 | curl -s https://raw.githubusercontent.com/BassT23/Proxmox/beta/update.sh > /root/update_beta.sh
28 | curl -s https://raw.githubusercontent.com/BassT23/Proxmox/develop/update.sh > /root/update_develop.sh
29 | MASTER_VERSION=$(awk -F'"' '/^VERSION=/ {print $2}' /root/update_master.sh)
30 | BETA_VERSION=$(awk -F'"' '/^VERSION=/ {print $2}' /root/update_beta.sh)
31 | DEVELOP_VERSION=$(awk -F'"' '/^VERSION=/ {print $2}' /root/update_develop.sh)
32 | LOCAL_VERSION=$(awk -F'"' '/^VERSION=/ {print $2}' $LOCAL_FILES/update.sh)
33 | if [[ "$BRANCH" == develop ]]; then
34 | if [[ ! -s /root/update_develop.sh ]]; then
35 | echo -e "${OR}*** You are offline - can't check version ***${CL}\n"
36 | echo -e "${OR}*** The Ultimate Updater is on develop branch ***${CL}"
37 | elif [[ "$LOCAL_VERSION" < "$MASTER_VERSION" ]]; then
38 | echo -e "${OR} *** A newer version is available ***${CL}\n\
39 | Installed: $LOCAL_VERSION / Github-Master: $MASTER_VERSION\n\
40 | ${OR}You can update with ${CL}\n"
41 | VERSION_NOT_SHOW=true
42 | elif [[ "$LOCAL_VERSION" < "$BETA_VERSION" ]]; then
43 | echo -e "${OR} *** A newer version is available ***${CL}\n\
44 | Installed: $LOCAL_VERSION / Github-Beta: $BETA_VERSION\n\
45 | ${OR}You can update with ${CL}\n"
46 | VERSION_NOT_SHOW=true
47 | elif [[ "$LOCAL_VERSION" < "$DEVELOP_VERSION" ]]; then
48 | echo -e "${OR} *** A newer version is available ***${CL}\n\
49 | Installed: $LOCAL_VERSION / Github-Develop: $DEVELOP_VERSION\n\
50 | ${OR}You can update with ${CL}\n"
51 | VERSION_NOT_SHOW=true
52 | else
53 | echo -e "${GN} The Ultimate Updater is UpToDate${CL}"
54 | fi
55 | fi
56 | if [[ "$BRANCH" == beta ]]; then
57 | if [[ ! -s /root/update_beta.sh ]]; then
58 | echo -e "${OR}*** You are offline - can't check version ***${CL}\n"
59 | echo -e "${OR}*** The Ultimate Updater is on beta branch ***${CL}"
60 | elif [[ "$LOCAL_VERSION" < "$MASTER_VERSION" ]]; then
61 | echo -e "${OR} *** A newer version is available ***${CL}\n\
62 | Installed: $LOCAL_VERSION / Github-Master: $MASTER_VERSION\n\
63 | ${OR}You can update with ${CL}\n"
64 | VERSION_NOT_SHOW=true
65 | elif [[ "$LOCAL_VERSION" < "$BETA_VERSION" ]]; then
66 | echo -e "${OR} *** A newer version is available ***${CL}\n\
67 | Installed: $LOCAL_VERSION / Github-Beta: $BETA_VERSION\n\
68 | ${OR}You can update with ${CL}\n"
69 | VERSION_NOT_SHOW=true
70 | elif [[ "$LOCAL_VERSION" < "$DEVELOP_VERSION" ]]; then
71 | echo -e "${OR} *** A newer version is available ***${CL}\n\
72 | Installed: $LOCAL_VERSION / Github-Develop: $DEVELOP_VERSION\n\
73 | ${OR}You can update with ${CL}\n"
74 | VERSION_NOT_SHOW=true
75 | else
76 | echo -e "${GN} The Ultimate Updater is UpToDate${CL}"
77 | fi
78 | fi
79 | if [[ "$BRANCH" == master ]]; then
80 | if [[ ! -s /root/update_master.sh ]]; then
81 | echo -e "${OR}*** You are offline - can't check version ***${CL}\n"
82 | elif [[ "$LOCAL_VERSION" < "$MASTER_VERSION" ]]; then
83 | echo -e "${OR} *** A newer version is available ***${CL}\n\
84 | Installed: $LOCAL_VERSION / Server: $MASTER_VERSION\n\
85 | ${OR}You can update with ${CL}\n"
86 | VERSION_NOT_SHOW=true
87 | else
88 | echo -e "${GN} The Ultimate Updater is UpToDate${CL}"
89 | fi
90 | fi
91 | if [[ "$VERSION_NOT_SHOW" != true ]]; then echo -e " Version: $LOCAL_VERSION\n"; fi
92 | rm -rf /root/update_master.sh
93 | rm -rf /root/update_beta.sh
94 | rm -rf /root/update_develop.sh
95 | }
96 |
97 | READ_WRITE_CONFIG () {
98 | WITH_HOST=$(awk -F'"' '/^CHECK_WITH_HOST=/ {print $2}' $CONFIG_FILE)
99 | WITH_LXC=$(awk -F'"' '/^CHECK_WITH_LXC=/ {print $2}' $CONFIG_FILE)
100 | WITH_VM=$(awk -F'"' '/^CHECK_WITH_VM=/ {print $2}' $CONFIG_FILE)
101 | RUNNING=$(awk -F'"' '/^CHECK_RUNNING_CONTAINER=/ {print $2}' $CONFIG_FILE)
102 | STOPPED=$(awk -F'"' '/^CHECK_STOPPED_CONTAINER=/ {print $2}' $CONFIG_FILE)
103 | EXCLUDED=$(awk -F'"' '/^EXCLUDE_UPDATE_CHECK=/ {print $2}' $CONFIG_FILE)
104 | ONLY=$(awk -F'"' '/^ONLY_UPDATE_CHECK=/ {print $2}' $CONFIG_FILE)
105 | if [[ $ONLY != "" ]]; then
106 | echo -e "${OR}Only is set. Not all machines are checked.${CL}\n"
107 | elif [[ $ONLY == "" && $EXCLUDED != "" ]]; then
108 | echo -e "${OR}Exclude is set. Not all machines are checked.${CL}\n"
109 | elif [[ $WITH_HOST != true || $WITH_LXC != true || $WITH_VM != true ||$RUNNING != true || $STOPPED != true ]]; then
110 | echo -e "${OR}The variable is set in config file. Some machines will not be checked!${CL}\n"
111 | fi
112 | }
113 |
114 | TIME_CALCULTION () {
115 | MOD=$(date -r "$LOCAL_FILES/check-output" +%s)
116 | NOW=$(date +%s)
117 | DAYS=$(( (NOW - MOD) / 86400 ))
118 | HOURS=$(( (NOW - MOD) / 3600 ))
119 | MINUTES=$(( (NOW - MOD) / 60 ))
120 | }
121 |
122 | # Welcome
123 | if [[ -f /usr/bin/neofetch ]]; then
124 | echo
125 | neofetch
126 | else
127 | echo
128 | fi
129 | VERSION_CHECK
130 | READ_WRITE_CONFIG
131 | if [[ -f $LOCAL_FILES/check-output ]]; then
132 | TIME_CALCULTION
133 | if [[ $DAYS -gt 0 ]]; then
134 | echo -e " Last Update Check: $DAYS day(s) ago\n"
135 | elif [[ $HOURS -gt 0 ]]; then
136 | echo -e " Last Update Check: $HOURS hour(s) ago\n"
137 | else
138 | echo -e " Last Update Check: $MINUTES minute(s) ago\n"
139 | fi
140 | if [[ -f $LOCAL_FILES/check-output ]] && [[ $CHECK_OUTPUT -gt 0 ]]; then
141 | echo -e "${OR}Available Updates:${CL}"
142 | echo -e "S = Security / N = Normal"
143 | cat $LOCAL_FILES/check-output
144 | fi
145 | echo
146 | fi
147 |
148 | exit 0
149 |
--------------------------------------------------------------------------------