· 6 years ago · Nov 01, 2019, 08:19 AM
1#!/bin/bash
2
3UEFIDracutUpdate() {
4 # MAIN FUNCTION TO CREATE / UPDATE BOOTABLE EFI FILES
5 #
6 # Inputs are kernel versions to update the bootable efi files for (same format as $(uname -r))
7 # If no (non-flag) inputs, all kernel versions will be updated
8 #
9 # This can be called with no arguments and **should** figure them all out for you using information from current mounts.
10 # ** It works well for me, but has only tested on configurations ive had on my system. This means:
11 #
12 # FIlesystems: ext4 and ZFS (on root) have been tested. XFS will probably work too.
13 # brtfs probably wont work without a little modification (though maybe it will, idk)
14 #
15 # GPU: tested with nvidia GPU using nvidia (not nouveau) drivers. integrated graphics probably works too.
16 # no special cases have been setup for AMD gpus.
17 #
18 # Other: LUKS should be handled appropriately. LVM and DM/MD RAID support has not been explicitly added.
19 #
20 # Fedora 29 (KDE) and 31 (KDE) are the only Fedora versions tested. I imagine it works with others, but idk for sure.
21 #
22 # Including files in the initramfs: 3 directories will be automatically added to the initramfs (if they exist and contain files):
23 #
24 # /usr/local/bin/my-services - this is intended for files that support the custom systemd services
25 #
26 # /usr/local/bin/my-scripts - this is intended for general scripts, including functions you might find
27 # useful should you end up in the dracut emergency shell for whatever reason.
28 #
29 # /etc/systemd/personal - optional directory to store personal systemd services
30 #
31 # Additionally, files needed for TPM2 decryption via TPM2_unseal, as well as kernel modules in the "extra" folder
32 # (e.g., the ones used by ZFS and nvidia) are included and installed
33 #
34 # NOTE: because this sets up the BOOT and ROOT devices in the kernel commandline, these devices need not be listed in /etc/fstab
35 # (nor, if using LUKS, in /etc/crypttab). However, any other devices should still be listed in /etc/{fs,crypt}tab.
36 # if you do have them listed there is can, in some instances, lead to issues
37 # # example: it might ask you a 2nd time for a password to decrypt an already-decrypted drive
38 #
39 # FLAGS: unless you use either of the flags '-na' or '--no-ask', you will be asked for
40 # confirmation that the parameters it has determined automatically are correct.
41 # if it is getting something wrong, it *should* accept any manual hardcoded entries of the following parameters:
42 # ${luksUUID} ${bootDisk} ${rootDisk} ${rootFSType} ${rootFlags} ${usrDisk} ${usrFSType} ${usrFlags}
43 #
44 # IMPORTANT NOTE: the variable 'kernelCmdline_static' is for all the "extra" kernel command line parameters you want to add.
45 # I left what Im using in there, as an example, though this likely wont be ideal for most systems.
46 # You should probably set this manually, or remove it / comment it out it entirely if you arent sure how to set up.
47 #
48 # NOTE: these codes probably require root privileges to run, though doesnt call sudo in case someone without root access wants to try it.
49 # I recommend, if possible, to run this as the root user (e.g., call 'su' then run it)
50
51 # set local variables
52
53 local rootDisk="";
54 local bootDisk="";
55 local luksUUID="";
56 local rootFSType="";
57 local rootFlags="";
58 local userResponse="";
59 local noAskFlag=0;
60 local kernelCmdline_static="";
61 local dracutArgsExtra=""
62 local nn=""
63 local mm=""
64 local ll=""
65 local usrDisk=""
66 local usrFSType=""
67 local usrFlags=""
68 local usrMountFlag=0
69 local kernelCmdline_spectreMeltdownMitigations=""
70 local -a kernels
71 local kernelsAvailable
72 local -a dracutArgsExtraKmod
73 local usrMountFlag=0
74 local useHardcodedZFSInfoFlag=0
75
76 # disable selinux when running from a live os
77 [[ -n "$(findmnt / | grep '/dev/mapper/live')" ]] && setenforce 0
78
79 kernelCmdline_static='rd.driver.pre=tpm rd.timeout=60 rd.locale.LANG=en_US.UTF-8 rd.udev.log-priority=info rd.lvm=0 rd.lvm.conf=0 rd.md=0 rd.md.conf=0 rd.dm=0 rd.dm.conf=0 systemd.unified_cgroup_hierarchy gpt intel_pstate=hwp_only zswap.enabled=1 zswap.compressor=lz4 transparent_hugepages=always panic=60 init=/usr/lib/systemd/systemd'
80
81 kernelCmdline_spectreMeltdownMitigations='spec_store_bypass_disable=prctl noibrs noibpb spectre_v2=auto pti=auto'
82
83 dracutArgsExtra='--persistent-policy "by-id" --no-hostonly-i18n --no-hostonly --nostrip --hostonly-cmdline --early-microcode --nohardlink --no-machineid --install /bin/vim --install /bin/tee --install /bin/seq --install /bin/find --install /bin/env --install /bin/wc --install /bin/tail --install /bin/head --force-add crypt --force-add dm --force-add zfs --force-add bash --force-add dracut-systemd --force-add systemd --force-add uefi-lib --force-add kernel-modules --force-add kernel-modules-extra --force-add iscsi --add plymouth --include /etc/iscsi/initiatorname.iscsi /etc/iscsi/initiatorname.iscsi --include /run/cryptsetup /run/cryptsetup'
84
85 # parse inputs
86 kernelsAvailable="$(ls -1 /usr/lib/modules)"
87 for nn in "${@}"; do
88 [[ ${nn,,} =~ ^(.+\ )?-+no?-?a(sk)?(\ .+)?$ ]] && noAskFlag=1
89 echo "${kernelsAvailable}" | grep -q "${nn}" && kernels[${#kernels[@]}]="${nn}";
90 done
91 [[ -z "${kernels[@]}" ]] && kernels=( ${kernelsAvailable} )
92 [[ -z "${kernels[@]}" ]] && echo "ERROR: no kernels could be found at /usr/lib/modules/*" >&2 && return 1
93
94 # ensure EFI is mounted
95 cat /proc/mounts | grep -q "$(myFindEFIDev)" || mount "$(myFindEFIDev)" 2>&1
96
97 # set parameters for root / boot / usr / luks disk information
98
99 # example of manually set parameters. Note that the leading '[param]=' part is auto added, and doesnt need to be included (though can be)
100
101 if (( ${useHardcodedZFSInfoFlag} == 1 )); then
102 rootDisk='ZFS=FEDORA/ROOT'
103 rootFSType='zfs'
104 rootFlags='zfsutil,rw,relatime,xattr,posixacl'
105 bootDisk='/dev/disk/by-id/nvme-Samsung_SSD_970_PRO_1TB_S462NF0K403461K-part1'
106 luksUUID='luks-2aa2788a-59fe-4340-9335-3631d8193f59'
107 #luksUUID="luks-$(cryptsetup luksUUID /dev/disk/by-id/nvme-Samsung_SSD_970_PRO_1TB_S462NF0K403461K-part2)"
108 #usrDisk='ZFS=FEDORA/ROOT/USR'
109 #usrFSType='zfs'
110 #usrFlags='zfsutil,rw,relatime,xattr,posixacl'
111 fi
112
113 # automatically find parameters for root, boot and luks if they are empty (i.e., not hard-coded)
114
115 [[ -z "${rootDisk}" ]] && [[ -n "$(mount -l | grep 'on / type' | sed -E s/'^(.*) on \/ type .*$'/'\1'/)" ]] && rootDisk="$(mount -l | grep 'on / type' | sed -E s/'^(.*) on \/ type .*$'/'\1'/)";
116
117 [[ -z "${bootDisk}" ]] && bootDisk="$(myFindEFIDev)"
118
119 [[ -z "${luksUUID}" ]] && luksUUID="$(myDevToID -u "$(echo "$(for nn in /dev/mapper/*; do [[ $(cryptsetup status "$nn" 2>&1) != *"not found" ]] && cryptsetup status "$nn" 2>/dev/null; done)" | grep 'device: ' | sed -E s/'^.*(\/dev\/.*)$'/'\1'/)" | sed -E s/'^UUID='/'luks-'/ | grep -vE '^luks-$')"
120 (( $(echo "${luksUUID}" | wc -l) > 1 )) && (( ${useHardcodedZFSInfoFlag} == 0 )) && luksUUID="$(echo "${luksUUID}" | while read -r nn; do [[ -n "$(dmsetup table "${nn}" | grep 'logon')" ]] && echo "${nn}"; done)"
121
122 [[ -z "${rootFSType}" ]] && [[ -n "$(mount -l | grep 'on / type' | sed -E s/'^.* on \/ type ([^ ]*) .*$'/'\1'/)" ]] && rootFSType="$(mount -l | grep 'on / type' | sed -E s/'^.* on \/ type ([^ ]*) .*$'/'\1'/)";
123 [[ "${rootDisk}" == "ZFS="* ]] && rootFSType='zfs'
124 [[ ${rootFSType#*=} =~ ^[Zz][Ff][Ss]$ ]] && rootFSype="zfs" && [[ -n "${rootDisk}" ]] && rootDisk="ZFS=$(echo "${rootDisk}" | sed -E s/'^[Zz][Ff][Ss]=(.*)'/'\1'/)"
125
126 [[ -z "${rootFlags}" ]] && [[ -n "$(mount -l | grep 'on / type' | sed -E s/'^.* on \/ type [^ ]* \(([^\)]*)\).*$'/'\1'/)" ]] && rootFlags="$(mount -l | grep 'on / type' | sed -E s/'^.* on \/ type [^ ]* \(([^\)]*)\).*$'/'\1'/)";
127 [[ ${rootFSType#*=} =~ ^[Zz][Ff][Ss]$ ]] && rootFlags="${rootFlags//'seclabel'/}" && [[ ! ${rootFlags#'rootFlags='} =~ ^(.+,)?zfsutil(,.+)?$ ]] && rootFlags="zfsutil,${rootFlags#'rootFlags='}"
128 [[ -n "${rootFlags}" ]] && rootFlags="${rootFlags//,,/,}"
129
130 # add in leading '[param]='
131
132 [[ -n "${rootDisk}" ]] && rootDisk="root=$(myDevToID "${rootDisk#'root='}") rw";
133 [[ -n "${bootDisk}" ]] && bootDisk="boot=$(myDevToID "${bootDisk#'boot='}")";
134 [[ -n "${luksUUID}" ]] && luksUUID="${luksUUID,,}" && luksUUID="${luksUUID#'*luks.uuid='}" && luksUUID="luks-${luksUUID#'luks-'}" && luksUUID="rd.luks.uuid=${luksUUID#'*luks.uuid='}" # && [[ "${luksUUID}" == "rd.luks.uuid=UUID" ]] && luksUUID="";
135 [[ -n "${rootFSType}" ]] && rootFSType="rootfstype=${rootFSType#'rootfstype='}";
136 [[ -n "${rootFlags}" ]] && rootFlags="rootflags=${rootFlags#'rootflags='}";
137
138 # same steps as above, but for a separate /usr mount (if it exists)
139
140 ( [[ -n "$(cat /etc/mtab | grep ' /usr ')" ]] || [[ -n "${usrDisk}${usrFSType}${usrFlags}" ]] ) && usrMountFlag=1
141
142
143 if (( $usrMountFlag == 1 )); then
144
145 [[ -z "${usrDisk}" ]] && [[ -n "$(mount -l | grep 'on /usr type' | sed -E s/'^(.*) on \/usr type .*$'/'\1'/)" ]] && usrDisk="$(mount -l | grep 'on /usr type' | sed -E s/'^(.*) on \/usr type .*$'/'\1'/)";
146
147 [[ -z "${usrFSType}" ]] && [[ -n "$(mount -l | grep 'on /usr type' | sed -E s/'^.* on \/usr type ([^ ]*) .*$'/'\1'/)" ]] && usrFSType="$(mount -l | grep 'on /usr type' | sed -E s/'^.* on \/usr type ([^ ]*) .*$'/'\1'/)";
148 [[ "${usrDisk}" == 'ZFS='* ]] && usrFSType='zfs'
149 [[ ${usrFSType#*=} =~ ^[Zz][Ff][Ss]$ ]] && usrFSype="zfs" && [[ -n "${usrDisk}" ]] && usrDisk="ZFS=$(echo "${usrDisk}" | sed -E s/'^[Zz][Ff][Ss]=(.*)'/'\1'/)"
150
151 [[ -z "${usrFlags}" ]] && [[ -n "$(mount -l | grep 'on /usr type' | sed -E s/'^.* on \/usr type [^ ]* \(([^\)]*)\).*$'/'\1'/)" ]] && usrFlags="$(mount -l | grep 'on /usr type' | sed -E s/'^.* on \/usr type [^ ]* \(([^\)]*)\).*$'/'\1'/)";
152 [[ ${usrFSType#*=} =~ ^[Zz][Ff][Ss]$ ]] && [[ ! ${usrFlags#'mount.usrflags='} =~ ^(.+,)?zfsutil(,.+)?$ ]] && usrFlags="zfsutil,${usrFlags#'mount.usrflags='}"
153
154 [[ -n "${usrDisk}" ]] && usrDisk="mount.usr=$(myDevToID "${usrDisk#'mount.usr='}")";
155 [[ -n "${usrFSType}" ]] && usrFSType="mount.usrfstype=${usrFSType#'mount.usrfstype='}";
156 [[ -n "${usrFlags}" ]] && usrFlags="mount.usrflags=${usrFlags#'mount.usrflags='}";
157 fi
158
159 # automatically make additions to the command line that are specific to using a nvidia gpu, using LUKS, and using ZFS
160
161 rpm -qa | grep -q 'nvidia' && kernelCmdline_static="${kernelCmdline_static} rd.driver.blacklist=nouveau rd.modprobe.blacklist=nouveau rd.driver.pre=nvidia rd.driver.pre=nvidia_uvm rd.driver.pre=nvidia_drm rd.driver.pre=drm rd.driver.pre=nvidia_modeset driver.blacklist=nouveau modprobe.blacklist=nouveau driver.pre=nvidia driver.pre=nvidia_uvm driver.pre=nvidia_drm driver.pre=drm driver.pre=nvidia_modeset nvidia-drm.modeset=1"
162
163 [[ -n "${luksUUID}" ]] && kernelCmdline_static="${kernelCmdline_static} rd.driver.pre=dm_crypt driver.pre=dm_crypt rd.luks.allow-discards rd.luks.timeout=60"
164 rpm -qa | grep -q 'zfs' && kernelCmdline_static="${kernelCmdline_static} zfs.zfs_flags=0x1D8 zfs_ignorecache=1"
165
166 [[ ${rootFSType#*=} =~ ^[Zz][Ff][Ss]$ ]] && kernelCmdline_static="${kernelCmdline_static} rd.driver.pre=zfs driver.pre=zfs zfs_force=1"
167
168 kernelCmdline_static="${kernelCmdline_static} systemd.show_status rd.info rd.shell rd.driver.pre=vfat rhgb"
169
170 kernelCmdline="${luksUUID} ${bootDisk} ${rootDisk} ${rootFSType} ${rootFlags} ${usrDisk} ${usrFSType} ${usrFlags} ${kernelCmdline_spectreMeltdownMitigations} ${kernelCmdline_static}";
171 kernelCmdline="$(echo "${kernelCmdline}" | sed -E s/'^[ \t]*([^ \t].*[^ \t])[ \t]*$'/'\1'/ | sed -E s/'[ \t]+'/' '/g)";
172
173 # include "extra" kernel modules
174 ll=0
175 for nn in "${kernels[@]}"; do
176 [[ -d "/usr/lib/modules/${nn}" ]] && for mm in /usr/lib/modules/${nn}/extra/{,*/}*.{ko,ko.xz}; do
177 [[ "${mm}" != *'*'* ]] && dracutArgsExtraKmod[$ll]+=" --include ${mm} ${mm} --install ${mm} ";
178 done
179 dracutArgsExtraKmod[$ll]="${dracutArgsExtraKmod[$ll]// / }"
180 ll=$((( $ll + 1 )))
181 done
182
183 for mm in {/usr/bin/tpm2*,/usr/lib*/*tcti*,/usr/lib/systemd/systemd}; do
184 dracutArgsExtra="${dracutArgsExtra} --include ${mm} ${mm} --install ${mm}";
185 done
186
187 # include items from /usr/local/my-{scripts,services}
188 for nn in /etc/systemd/personal/ /usr/local/bin/my-{scripts,services}/ /usr/local/bin/my-{scripts,services}/*.sh /etc/systemd/personal/*{.sh,.service}; do
189 dracutArgsExtra="${dracutArgsExtra} --include ${nn} ${nn}"
190 done
191
192 # include scripts from ~/.bash{rc,_aliases,_functions}
193 for nn in {'.bashrc','.bash_functions','.bash_aliases'}; do
194 [[ -f ~/${nn} ]] && dracutArgsExtra="${dracutArgsExtra} --include $(realpath ~/${nn}) /usr/local/bin/homedir_scripts/${nn}"
195 done
196
197 # include [+install?] items from /etc/systemd/personal into both /etc/systemd/personal and /etc/systemd/system in the initrd
198 if [[ -d "/etc/systemd/personal" ]]; then
199 for nn in /etc/systemd/personal/*{.sh,.service}; do
200 [[ "${nn}" == *.service ]] && chmod -x "${nn}" || chmod +x "${nn}"
201 \cp -f "${nn}" "/usr/lib/systemd/system/";
202
203 dracutArgsExtra="${dracutArgsExtra} --include ${nn} ${nn//\/personal\//\/system\/}";
204 dracutArgsExtra="${dracutArgsExtra} --include ${nn} ${nn//\/personal\//\/user\/}";
205
206 #dracutArgsExtra="${dracutArgsExtra} --install ${nn}";
207 #[[ -e "${nn//\/personal\//\/system\/}" ]] && dracutArgsExtra="${dracutArgsExtra} --install ${nn//\/personal\//\/system\/}";
208 #[[ -e "${nn//\/personal\//\/user\/}" ]] && dracutArgsExtra="${dracutArgsExtra} --install ${nn//\/personal\//\/user\/}";
209 done
210 fi
211
212 # manually specify boot stub location
213 [[ -e "/usr/lib/systemd/boot/efi/linuxx64.efi.stub" ]] && dracutArgsExtra="${dracutArgsExtra} --uefi-stub /usr/lib/systemd/boot/efi/linuxx64.efi.stub"
214
215 # Print info to screen about the parsed parameters.
216 # If '--no-ask' flag is set, pause for 10 seconds then continue.
217 # Otherwise, pause for a minute and request user feedback.
218 # Note: if no feedbback is received in 1 minute, the code will continue running.
219
220 echo -e "\n\n||---------- THE FOLLOWING SETTINGS HAVE BEEN SET ----------||\n"
221 (( $noAskFlag == 0 )) && echo -e "Do these look correct? \n----> An empty/null response or a response of y[es] will be taken as \"yes\" \n----> Any other non-empty response will be taken as \"no\" \n----> Timeout is 60 seconds. Default response upon timeout is \"yes\" \n"
222 echo -e " ${rootDisk} $( [[ -n "${rootDisk}" ]] && echo '\n ' )${rootFSType}$( [[ -n "${rootFSType}" ]] && echo '\n ' )${rootFlags}$( [[ -n "${rootFlags}" ]] && echo '\n ' )${bootDisk}$( [[ -n "${bootDisk}" ]] && echo '\n ' )${luksUUID}$( [[ -n "${luksUUID}" ]] && echo '\n ' )${usrDisk}$( [[ -n "${usrDisk}" ]] && echo '\n ' )${usrFSType}$( [[ -n "${usrFSType}" ]] && echo '\n ' )${usrFlags}$( [[ -n "${usrFlags}" ]] && echo '\n' ) \nADDITIONAL DRACUT PARAMATERS: -fvM --uefi ${dracutArgsExtra} ${dracutArgsExtraKmod[*]} \n\nKERNEL COMMAND LINE: \n\n${kernelCmdline} \n";
223
224 (( $noAskFlag == 0 )) && read -iE -t 60 -p "Response ( <null>/y[es] --> yes | anything else --> no ): " userResponse && [[ -n "${userResponse}" ]] && [[ ! ${userResponse,,} =~ ^y(es)?$ ]] && [[ ! ${userResponse} =~ ^[\ \t]*$ ]] && return 1;
225 (( $noAskFlag == 1 )) && echo "The code will proceed in 10 seconds..." && sleep 10;
226
227 # shift efi files and ensure systemd is up to date systemd
228 UEFIBootUpdate --shift-efi;
229
230 if [[ "$(systemctl get-default)" == "graphical.target" ]]; then
231 systemctl set-default graphical.target;
232 systemctl set-default graphical.target --global --user;
233 systemctl set-default graphical.target --global --system;
234 fi
235 systemctl daemon-reload;
236 systemctl daemon-reload --global --system;
237 systemctl daemon-reexec --global --system;
238
239 # run dracut --uefi, either for selected kernels
240
241 ll=0
242 for nn in "${kernels[@]}"; do
243 ( [[ -e "/usr/lib/modules/${nn}" ]] || [[ -e "/usr/src/kernels/${nn}" ]] ) && dracut -fvM --uefi --kernel-cmdline "${kernelCmdline}" --kver "${nn}" ${dracutArgsExtra} ${dracutArgsExtraKmod[$ll]};
244 ll=$((( $ll + 1 )))
245 done
246
247 UEFIBootUpdate
248
249}
250
251
252myFindEFIDev() {
253 # attempts to automatically find the block device the system EFI partition is on
254 # scans /etc/fstab and /etc/mtab for any vfat filesystem mounts on /boot, /boot/efi, or /efi
255
256 local -a EFIdev
257 local nn=0
258 #local EFIout=""
259
260 EFIdev=($(echo "$(cat /etc/fstab /etc/mtab /proc/mounts | grep 'vfat' | grep -E '[ \t](((\/[Bb][Oo][Oo][Tt])?\/[Ee][Ff][Ii])|\/[Bb][Oo][Oo][Tt])[ \t]' | sed -E s/'^[ \t]*([^ \t]*)[ \t].*$'/'\1'/ | sort -u)"))
261
262 for nn in $(seq 0 $((( ${#EFIdev[@]} - 1 )))); do
263 [[ ${EFIdev[$nn]^^} =~ ^UUID=[0-9A-Z-]+$ ]] && EFIdev[$nn]="$(findmnt ${EFIdev[$nn]} | tail -n $((( $(findmnt ${EFIdev[$nn]} | wc -l) - 1 ))) | grep -E '(((\/[Bb][Oo][Oo][Tt])?\/[Ee][Ff][Ii])|\/[Bb][Oo][Oo][Tt])[ \t]' | sed -E s/'^[^ \t]+[ \t]+([^ \t]*)[ \t].*$'/'\1'/)"
264
265 [[ ${EFIdev[$nn]} =~ ^\/dev\/disk\/by-id\/.+$ ]] && EFIdev[$nn]="$(echo "/dev/$(ls -l1 /dev/disk/by-id | grep "$(echo "${EFIdev[$nn]}" | sed -E s/'^\/dev\/disk\/by\-id\/(.*)$'/'\1'/)" | head -n 1 | sed -E s/'^.* -> \.\.\/\.\.\/(.*)$'/'\1'/)")"
266 done
267
268 EFIdev=($(echo "${EFIdev[@]}" | sed -z s/' '/'\n'/g | sort -u))
269
270 if (( ${#EFIdev[@]} == 0 )); then
271 echo "$(local nn=""; for nn in "${EFIdev[@]}"; do [[ -n "$(findmnt "${nn}" | grep 'vfat' | grep -E '(((\/[Bb][Oo][Oo][Tt])?\/[Ee][Ff][Ii])|\/[Bb][Oo][Oo][Tt]))')" ]] && echo "${nn}"; done | sort -u)"
272 else
273 echo "${EFIdev[0]}"
274 fi
275
276 #[[ -n "${EFIout}" ]] && echo "${EFIout}" || ( [[ ! "${1,,}" =~ ^-+m(ou)?nt(-?pnt)?$ ]] && tee || while read -r nn; do findmnt "${nn}" | tail -n -1 | sed -E s/'^([^ \t]*).*$'/'\1'/; done )
277
278}
279
280
281myDevToID() {
282 # find a unique block device name from /dev/disk/by-id or a unique uuid for the device
283 # input is block device ( /dev/<...> )
284 # output is the (full path of the) matching entry in /dev/disk/by-id/<...>, UNLESS using uuid flag
285 # if flag '-u' or '--uuid' is used as 1st input, output will instead be 'UUID=<deviceUUID>'
286 # if flag --type=<...> is used, where <...>=[by-]{partuuid,partlabel,path,id,label,uuid}; then the corresponding folder in /dec/disk will be used. Note that using 'uuid' here outputs the path in /dev/disk/uuid, NOT 'UUID=<deviceUUID>'
287
288 local uuidFlag=0
289 local matchType=""
290 local -a inAll0
291 local inAll=""
292 local nn=""
293
294 inAll0=("${@}")
295
296 for nn in $(seq 0 $((( ${#inAll0[@]} - 1 )))); do
297
298 if [[ ${inAll0[$nn],,} =~ ^-+u(uid)?$ ]]; then
299 uuidFlag=1 && matchType='uuid' && inAll0[$nn]=""
300
301 elif [[ ${inAll0[$nn],,} =~ ^-+type=(by-)?(id|uuid|partuuid|partlabel|label|path)$ ]]; then
302 [[ "${matchType}" != 'uuid' ]] && matchType="$(echo "${inAll0[$nn],,}" | sed -E s/'^-+type=["'"'"']?(by-)?(id|uuid|partuuid|partlabel|label|path)["'"'"']?$'/'\2'/)" && inAll0[$nn]=""
303
304 else
305 inAll="$(cat <(echo "${inAll}") <(echo "${inAll0[$nn]}"))"
306 fi
307 done
308
309 inAll="$(echo "${inAll}" | grep -vE '^$')"
310
311 [[ -z "${matchType}" ]] && matchType='id'
312
313 if (( $(echo "${inAll}" | wc -l) > 1 )); then
314
315 (( $uuidFlag == 1 )) && matchType='-u' || matchType='type=by-'"${matchType}"
316
317 echo "${inAll}" | while read -r nn; do
318 [[ -n "${nn}" ]] && myDevToID ${matchType} "${nn}"
319 done
320
321 else
322
323 [[ "${inAll}" == '/dev/mapper/'* ]] && echo "${*}" && return
324
325 [[ "${inAll^^}" =~ ^ZFS=.+$ ]] && echo "ZFS=${*##*=}" && return
326
327 (( $uuidFlag == 1 )) && [[ "${inAll}" == /dev/disk/by-uuid/* ]] && [[ -e "${inAll}" ]] && ( [[ -z "${*##*/}" ]] && echo "" || echo "UUID=${*##*/}" ) && return
328 (( $uuidFlag == 0 )) && [[ "${inAll}" == /dev/disk/by-${matchType}/* ]] && [[ -e "${inAll}" ]] && echo "${inAll}" && return
329
330 (( $uuidFlag == 0 )) && [[ "${inAll}" == /dev/disk/* ]] && [[ ! "${inAll}" == /dev/disk/by-${matchType}/* ]] && inAll="$(realpath "${inAll%/*}/$(ls -l1 ${inAll%/*} | grep -vE '(^nvme-eui|^nvme-nvme|^wwn)' | grep -F "${inAll##*/}" | sed -E s/'^.* [^ ]* -> (.*)$'/'\1'/ | sort -u | head -n 1)")"
331
332 (( $uuidFlag == 1 )) && echo "UUID=$(ls -l1 /dev/disk/by-uuid | grep -F "/${inAll##*/}" | head -n 1 | sed -E s/'^.* ([^ ]*) -> .*$'/'\1'/)"
333 (( $uuidFlag == 0 )) && echo "/dev/disk/by-${matchType}/$(ls -l1 /dev/disk/by-${matchType} | grep -v -E '(nvme\-eui|nvme\-nvme|wwn)' | grep -F "/${inAll##*/}" | head -n 1 | sed -E s/'^.* ([^ ]*) -> .*$'/'\1'/)"
334
335 fi
336}
337
338
339UEFIRemoveDuplicateBootEntries() {
340 # Remove duplicate entries from the EFI boot table
341
342 local nn
343 local mm
344 efibootmgr -v | sed -E s/'^(Boot.{5}) (.*)$'/'\2 \1'/ | sed -E s/'^(.*[^\*])\*$'/'\1'/ | sort -u | grep -Ev '(Boot((Current)|(Order)))|(Timeout)' | while read -r nn; do
345 [[ "${mm%Boot*}" == "${nn%Boot*}" ]] && efibootmgr -B -b "${nn##*Boot}";
346 mm="${nn}";
347 done
348}
349
350
351UEFIBootUpdate() {
352 # Generates new EFI boot menu entries for new .efi files detected in <EFI_DEV>/EFI/Linux/linux-*.efi
353 #
354 # FLAGS: use flag '--shift-efi' to instead rotate the current efi files (for a given kernel) into "*_OLD.efi" versions.
355 # existing "*_OLD.efi" versions will be overwritten.
356 #
357 # By default, the current .efi file (for a given kernel) will be copied into "*_WORKING.efi" IFF this file doesnt yet exist
358 #
359 # The standard names automatically replace the "_OLD" names before each every update, and thus are always remade
360 # and the "WORKING" names are left as is, and if they dont exist the current efi file will be made into the "_WORKING" efi file
361
362 local nn="";
363 local mm="";
364 local EFIcurmenu="";
365 local EFIbasedir="";
366 local EFIdev0="";
367 local EFIdev="";
368 local EFIpart="";
369 local EFIpartuuid=""
370 local kverEFI="";
371 local kverEFI0="";
372 local kverEFItemp="";
373 local kverKernel="";
374 local shiftEFIFlag=0;
375
376 [[ ${1,,} =~ ^-+s(hift)?-?(e(fi)?)?$ ]] && shiftEFIFlag=1 && shift 1
377
378 # find base dir for efi device. should be /boot, /boot/efi, or /efi, depending of the system setup
379
380 if (( $# > 0 )) && [[ -d "${1}" ]]; then
381 EFIbasedir="${1}";
382 else
383 EFIbasedir="$(findmnt $(myFindEFIDev) | grep -E '(((\/[Bb][Oo][Oo][Tt])?\/[Ee][Ff][Ii])|\/[Bb][Oo][Oo][Tt])[ \t]' | grep 'vfat' | sed -E s/'^([^ \t]*).*$'/'\1'/)"
384 if [[ ! -d "${EFIbasedir}" ]]; then
385 if [[ -d "/boot/efi/EFI" ]] &&
386 [[ -z "$(mountpoint "/boot/efi" | grep 'not')" ]]; then
387 EFIbasedir="/boot/efi";
388 elif [[ -d "/boot/EFI" ]] && [[ -z "$(mountpoint "/boot" | grep 'not')" ]]; then
389 EFIbasedir="/boot";
390 elif [[ -d "/efi/EFI" ]] && [[ -z "$(mountpoint "/efi" | grep 'not')" ]]; then
391 EFIbasedir="/boot";
392 elif [[ -d "/boot/efi" ]]; then
393 EFIbasedir="/booti/efi";
394 elif [[ -d "/EFI" ]]; then
395 EFIIbasedir="/";
396 elif [[ -d "/efi" ]]; then
397 EFIbasedir="/efi";
398 else
399 EFIbasedir="/boot/efi";
400 fi
401 fi
402 fi
403
404 # break into device + partition
405
406 EFIdev0="$(myDevToID "$(myFindEFIDev)")"
407 EFIpart="$(echo -n "${EFIdev0}" | sed -E s/'^.*[^0-9]([0-9]+)$'/'\1'/)";
408 EFIdev="$(echo -n "${EFIdev0}" | sed -E s/'^(.*)-part[0-9]*$'/'\1'/)";
409 EFIpartUUID="$(myDevToID --type=partuuid "${EFIdev0}")"
410 EFIpartUUID="${EFIpartUUID##*/}"
411
412 [[ -n "$(echo "${EFIdev}" | grep 'nvme')" ]] && EFIdev="${EFIdev%p}";
413
414 echo -e "\nThe following properties have been identified for the EFI system partition: \n Mount Point: \t ${EFIbasedir} \n Device Name: \t ${EFIdev} \n Partition: \t ${EFIpart} \n";
415 sleep 1;
416
417 # get list of current menu entries and kernel versions of items to add
418
419 EFIcurmenu="$(efibootmgr -v 2>&1)";
420
421 kverEFI="$(find "${EFIbasedir}" -type f -name "linux-*.efi" | grep -E '\/Linux\/[^\/]*$' | sort -V)";
422 [[ -z "${kverEFI}" ]] && echo "No .efi files found on the EFI Partition at '${EFIbasedir}/**/Linux/linux-*.efi'" >&2 && return 1
423
424 kverKernel="$(echo "$kverEFI" | sed -E s/'^.*\/linux-(.*'"$(uname -m)"').*\.efi$'/'\1'/ | sort -u)"
425
426 # check if *_WORKING efi boot file exists, if not copy current efi boot file into *_WORKING efi boot file
427
428 echo "${kverKernel}" | while read -r nn; do
429 kverEFITemp="$(echo "${kverEFI}" | grep "${nn}")"
430
431 # Copy into _OLD if shiftEFIFlag=1
432 (( $shiftEFIFlag == 1 )) && echo "${kverEFITemp}" | grep -qE "$(uname -m)"'(\-[0-9a-zA-Z]*)?\.efi$' && cp -f "${nn}" "${nn//$(uname -m)/$(uname -m)_OLD}"
433
434 # Copy into _WORKING if it doesnt already exist
435 echo "${kverEFITemp}" | grep -qE "$(uname -m)"'(\-[0-9a-zA-Z]*)?\.efi$' && echo "${kverEFITemp}" | grep -qE "$(uname -m)"'_WORKING(\-[0-9a-zA-Z]*)?\.efi$' || echo "${kverEFITemp}" | grep -E "$(uname -m)"'(\-[0-9a-zA-Z]*)?\.efi$' | while read mm; do
436 cp "${mm}" "${mm//$(uname -m)/$(uname -m)_WORKING}"
437 echo -e "\nSince no ${mm//$(uname -m)/$(uname -m)_WORKING} EFI file exists, \nthe EFI boot file ${mm} has been saved as a working backup\n" >&2;
438 done;
439 done
440
441 # update kverEFI with (possible) new copied entries
442 kverEFI="$(find "${EFIbasedir}" -type f -name "linux-*.efi" | grep -E '\/Linux\/[^\/]*$' | sort -V)";
443
444 kverEFI="$(cat <(echo "${kverEFI}" | grep '_OLD' | sort -V) <(echo "${kverEFI}" | grep '_WORKING' | grep -v '_OLD' | sort -V) <(echo "${kverEFI}" | grep -v '_WORKING' | grep -v '_OLD' | sort -V))"
445
446 # update efi boot order or (if --shift-efi flag set) shift the on-disk efi files
447
448 echo "${kverEFI}" | while read -r nn; do
449 #kverEFITemp="${nn,,}";
450 #kverEFItemp="$(echo "${EFIcurmenu,,}" | grep "${EFIpartUUID}" | grep "file(${kverEFITemp})")"
451
452 echo "${EFIcurmenu}" | grep "${EFIpartUUID}" | grep -q "$(echo -n 'File('"${nn##"$EFIbasedir"}" | sed -E s/'\/'/'\\\\'/g)" || echo "Adding boot entry for ${nn}" >&2 && efibootmgr -c -d "${EFIdev}" -p ${EFIpart} -l "$(echo -n "${nn##"$EFIbasedir"}")" -L "Fedora-EFI_$(echo -n "${nn##*linux-}" | sed -E s/'^([^-]*-[^-]*)(-[0-9a-zA-Z]*)?\.efi$'/'\1'/)" 1>/dev/null;
453 done
454
455 # remove duplicate entries
456 UEFIRemoveDuplicateBootEntries
457
458 # print new boot order (unless using --shift-efi)
459 (( $shiftEFIFlag == 1 )) || UEFIBootOrderList -v
460
461}
462
463UEFIBootOrderList() {
464 # prints a "nice" list of active and inactive boot entries. Basically, a poretty alternative to the output of efibootmgr
465 #
466 # FLAGS
467 #
468 # use flag '-v' or '--verbose' to give additional output (including info on what disk/partition/file the boot entry represents)
469 #
470 # use flag '-m' or '--mod[ify]' to reorder the default boot list order
471 # This will cause the boot list to be displayed, after which you will be interactively asked to choose entries to:
472 # a) deactivate b) send to the top of the active list
473
474 local nn="";
475 local EFImenu="";
476 local -a bootOrderVal;
477 local -a bootOrderIsActive;
478 local -a bootOrderInd;
479 local -a bootOrderReMap;
480 local activeIndList=""
481 local inactiveIndList=""
482 local bootCurTemp=""
483 local indCurTemp=""
484 local kk=0;
485 local kk1=0;
486
487 bootOrderInd=($(echo -n "$(efibootmgr | grep -E '^BootOrder\:' | sed -E s/'^BootOrder\: (.*)$'/'\1'/ | myuniq | sed -zE s/'\n'/' '/g)"));
488 [[ ${*,,} =~ ^(.+ )?-+no?-?d(edup)?( .+)?$ ]] || efibootmgr -o $(echo ${bootOrderInd[@]} | sed -E s/' '/','/g) 2>&1 1>/dev/null
489 EFImenu="$(efibootmgr $( [[ ${*,,} =~ ^(.+ )?-+v(erbose)?( .+)?$ ]] && echo '-v' ) | tail -n +4)";
490
491 kk=0
492 for nn in "${bootOrderInd[@]}"; do
493 bootCurTemp="$(echo "${EFImenu}" | grep --color=auto -E '^Boot'"${nn}"'[\* ] ')"
494 bootOrderVal[$kk]="$(echo "${bootCurTemp}" | sed -E s/'^Boot[^\* ]*[\* ] (.*)$'/'\1'/)";
495 [[ "${bootCurTemp%% *}" == *\* ]] && bootOrderIsActive[$kk]=1 && activeIndList="${activeIndList} ${kk}"
496 [[ ! "${bootCurTemp%% *}" == *\* ]] && bootOrderIsActive[$kk]=0 && inactiveIndList="${inactiveIndList} ${kk}"
497 ((kk++))
498 done
499
500 echo -e "\n||----------------------------------------------------------||\n||-------------------- EFI BOOT ENTRIES --------------------||\n||----------------------------------------------------------||\n";
501 echo -e "\n||------------------------- ACTIVE -------------------------||\n";
502
503 kk1=0
504 for kk in ${activeIndList}; do
505 echo "(${kk1}) ${bootOrderVal[$kk]}"
506 bootOrderReMap[$kk1]=${kk}
507 ((kk1++))
508 done
509
510 echo -e "\n\nNOTE: The above list is ordered by boot priority. The 1st listing is the \"default\" boot entry. \n The default behavior is to try these in order until a valid/working boot image is found.";
511
512 echo -e "\n\n||------------------------ INACTIVE ------------------------||\n";
513
514 for kk in ${inactiveIndList}; do
515 echo "(${kk1}) ${bootOrderVal[$kk]}"
516 bootOrderReMap[$kk1]=${kk}
517 ((kk1++))
518 done;
519 echo -e "\n\nNOTE: To activate/deactivate/reorder efi boot menu entries, run 'UEFIBootOrderList -m [-v]'\n\n"
520
521 if [[ ${*,,} =~ ^(.+ )?-+m(od(ify)?)?( .+)?$ ]]; then
522 local userResponse=""
523
524 echo -e "\nRespond to the following questions with a {space,comma,newline}-seperated list of indicies from the above listing. \nThe index is the number shown in (#) at the start of each line. \nNote: Non-numeric characters (except for spaces, commas and newlines) will be filtered out of the response and ignored. \n\nTimeout is 30 seconds\n"
525
526 read -ie -t 30 -p "Select boot entries to deactivate: " userResponse
527
528 userResponse="$(echo "${userResponse}" | sed -E s/'[ \n]'/' '/g | sed -E s/'[^0-9 ]'//g | sed -E s/' +'/' '/g | sed -E s/'^ $'//)"
529
530 if [[ -n "${userResponse}" ]]; then
531 for kk in ${userResponse}; do
532 (( $kk < ${#bootOrderReMap[@]} )) && efibootmgr -A -b "${bootOrderInd[${bootOrderReMap[$kk]}]}" 2>&1 1>/dev/null
533 done
534 fi
535
536 userResponse=""
537 read -ie -t 30 -p "Select boot entries to place at the top of the \"active\" list: " userResponse
538
539 userResponse="$(echo "${userResponse}" | sed -E s/'[ \n]'/' '/g | sed -E s/'[^0-9 ]'//g | sed -E s/' +'/' '/g | sed -E s/'^ $'//)"
540
541 if [[ -n "${userResponse}" ]]; then
542 local bootOrderNew=""
543 local -a bootOrderNewTemp
544 bootOrderNewTemp=("${bootOrderInd[@]}")
545 for kk in ${userResponse}; do
546 if (( $kk < ${#bootOrderReMap[@]} )); then
547 efibootmgr -a -b ${bootOrderInd[${bootOrderReMap[$kk]}]} #2>/dev/null
548 bootOrderNew="${bootOrderNew} ${bootOrderInd[${bootOrderReMap[$kk]}]}"
549 bootOrderNewTemp[${bootOrderReMap[$kk]}]=""
550 fi
551 done
552
553 bootOrderNew="$(echo "${bootOrderNew} ${bootOrderNewTemp[@]}" | sed -E s/'[ \t\n\,]+'/' '/g | sed -E s/'^ *([^ ].*[^ ]) *$'/'\1'/ | myuniq | sed -zE s/'\n'/','/g | sed -E s/'^(.*[^.]),*$'/'\1\n'/)"
554 efibootmgr -o "${bootOrderNew%,}" #2>&1 1>/dev/null
555 fi
556
557 echo -e "\n\n||----------------- NEW BOOT MENU ORDERING -----------------|| \n\n"
558 UEFIBootOrderList $( [[ ${*,,} =~ ^(.+ )?-+v(erbose)?( .+)?$ ]] && echo '-v' )
559 fi
560}
561
562myuniq() {
563 # returns unique elements in a list
564 # replacement for 'uniq', which is straight broken on my system
565 # input should be list separated by newlines/spaces/tabs/commas
566 # output is newline-separated list
567
568 local inAll=""
569 local outAll=""
570
571 (( $# > 0 )) && inAll="${@}" || inAll="$(cat <&0)"
572 inAll="$(echo -n "${inAll}" | sed -zE s/'[ ,\t]'/'\n'/g | sed -zE s/'\n+'/'\n'/g)"
573 while [[ -n "${inAll}" ]]; do
574 outAll="$(echo "${outAll}"; echo "${inAll}" | head -n 1)"
575 inAll="$(echo -n "${inAll}" | grep -v -F "$(echo "${inAll}" | head -n 1)")"
576 done
577
578 outAll="$(echo "${outAll}" | grep -E '.+')"
579 echo "${outAll}"
580}
581
582UEFIKernelPostInstallSetup() {
583 # setup kernel post install script to automatically re-make the bootable efi files after a kernel update
584 # untested, but i think it should work. Change '/usr/local/bin/my-scripts/dracut-UEFI-boot.sh' to wherever this file is actually saved.
585
586 cat << EOF > "/etc/kernel/postinst.d/dracut-EFI_postinst"
587#!/bin/bash
588
589. ${BASH_SOURCE[0]}
590
591(( \$# > 0 )) && UEFIDracutUpdate "\${@}" || UEFIDracutUpdate
592
593EOF
594
595 chmod 755 "/etc/kernel/postinst.d/dracut-EFI_postinst"
596
597}