/bin/check-missing-firmware is in ubiquity 2.21.63.
This file is owned by root:root, with mode 0o755.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 | #!/bin/sh
set -e
. /usr/share/debconf/confmodule
MISSING='/dev/.udev/firmware-missing /run/udev/firmware-missing'
DENIED=/tmp/missing-firmware-denied
if [ "x$1" = "x-n" ]; then
NONINTERACTIVE=1
else
NONINTERACTIVE=""
fi
IFACES="$@"
log () {
logger -t check-missing-firmware "$@"
}
# Not all drivers register themselves if firmware is missing; in that
# case determine the module via the device's modalias.
get_module () {
local devpath=$1
if [ -d $devpath/driver ]; then
# The real path of the destination of the driver/module
# symlink should be something like "/sys/module/e100"
basename $(readlink -f $devpath/driver/module) || true
elif [ -e $devpath/modalias ]; then
modalias="$(cat $devpath/modalias)"
# Take the last module returned by modprobe
modprobe --show-depends "$modalias" 2>/dev/null | \
sed -n -e '$s#^.*/\([^.]*\)\.ko.*$#\1#p'
fi
}
# Some modules only try to load firmware once brought up. So bring up and
# then down any interfaces specified by ethdetect.
upnics() {
for iface in $IFACES; do
log "taking network interface $iface up/down"
ip link set "$iface" up || true
ip link set "$iface" down || true
done
}
# Checks if a given module is a nic module and has an interface that
# is up and has an IP address. Such modules should not be reloaded,
# to avoid taking down the network after it's been configured.
nic_is_configured() {
module="$1"
for iface in $(ip -o link show up | cut -d : -f 2); do
dir="/sys/class/net/$iface/device/driver"
if [ -e "$dir" ] && [ "$(basename "$(readlink "$dir")")" = "$module" ]; then
if ip address show scope global dev "$iface" | grep -q 'scope global'; then
return 0
fi
fi
done
return 1
}
get_fresh_dmesg() {
dmesg_file=/tmp/dmesg.txt
dmesg_ts=/tmp/dmesg-ts.txt
# Get current dmesg:
dmesg > $dmesg_file
# Truncate if needed:
if [ -f $dmesg_ts ]; then
# Transform [foo] into \[foo\] to make it possible to search for
# "^$tspattern" (-F for fixed string doesn't play well with ^ to
# anchor the pattern on the left):
tspattern=$(cat $dmesg_ts | sed 's,\[,\\[,;s,\],\\],')
log "looking at dmesg again, restarting from $tspattern"
# Find the line number for the first match, empty if not found:
ln=$(grep -n "^$tspattern" $dmesg_file |sed 's/:.*//'|head -n 1)
if [ ! -z "$ln" ]; then
log "timestamp found, truncating dmesg accordingly"
sed -i "1,$ln d" $dmesg_file
else
log "timestamp not found, using whole dmesg"
fi
else
log "looking at dmesg for the first time"
fi
# Save the last timestamp:
grep -o '^\[ *[0-9.]\+\]' $dmesg_file | tail -n 1 > $dmesg_ts
log "saving timestamp for a later use: $(cat $dmesg_ts)"
# Write and clean-up:
cat $dmesg_file
rm $dmesg_file
}
check_missing () {
upnics
# Give modules some time to request firmware.
sleep 1
modules=""
files=""
# The linux kernel and udev no longer let us know via
# /dev/.udev/firmware-missing and /run/udev/firmware-missing
# which firmware files the kernel drivers look for. Check
# dmesg instead. See also bug #725714.
fwlist=/tmp/check-missing-firmware-dmesg.list
get_fresh_dmesg | sed -rn 's/^(\[[^]]*\] )?([^ ]+) [^ ]+: firmware: failed to load ([^ ]+) .*/\2 \3/p' > $fwlist
while read module fwfile ; do
log "looking for firmware file $fwfile requested by $module"
if [ ! -e /lib/firmware/$fwfile ] ; then
if grep -q "^$fwfile$" $DENIED 2>/dev/null; then
log "listed in $DENIED"
continue
fi
files="${files:+$files }$fwfile"
modules="$module${modules:+ $modules}"
fi
done < $fwlist
# This block looking in $MISSING should be removed when
# hw-detect no longer should support installing using older
# udev and kernel versions.
for missing_dir in $MISSING
do
if [ ! -d "$missing_dir" ]; then
log "$missing_dir does not exist, skipping"
continue
fi
for file in $(find $missing_dir -type l); do
# decode firmware filename as encoded by
# udev firmware.agent
fwfile="$(basename $file | sed -e 's#\\x2f#/#g')"
# strip probably nonexistant firmware subdirectory
devpath="$(readlink $file | sed 's/\/firmware\/.*//')"
# the symlink is supposed to point to the device in /sys
if ! echo "$devpath" | grep -q '^/sys/'; then
devpath="/sys$devpath"
fi
module=$(get_module "$devpath")
if [ -z "$module" ]; then
log "failed to determine module from $devpath"
continue
fi
rm -f "$file"
if grep -q "^$fwfile$" $DENIED 2>/dev/null; then
continue
fi
files="$fwfile${files:+ $files}"
if [ "$module" = usbcore ]; then
# Special case for USB bus, which puts the
# real module information in a subdir of
# the devpath.
for dir in $(find "$devpath" -maxdepth 1 -mindepth 1 -type d); do
module=$(get_module "$dir")
if [ -n "$module" ]; then
modules="$module${modules:+ $modules}"
fi
done
else
modules="$module${modules:+ $modules}"
fi
done
done
if [ -n "$modules" ]; then
log "missing firmware files ($files) for $modules"
return 0
else
log "no missing firmware in loaded kernel modules"
return 1
fi
}
# If found, copy firmware file; preserve subdirs.
try_copy () {
local fwfile=$1
local sdir file f target
sdir=$(dirname $fwfile | sed "s/^\.$//")
file=$(basename $fwfile)
for f in "/media/$fwfile" "/media/firmware/$fwfile" \
${sdir:+"/media/$file" "/media/firmware/$file"}; do
if [ -e "$f" ]; then
target="/lib/firmware${sdir:+/$sdir}"
log "copying loose file $file from '$(dirname $f)' to '$target'"
mkdir -p "$target"
rm -f "$target/$file"
cp -aL "$f" "$target" || true
break
fi
done
}
first_try=1
first_ask=1
ask_load_firmware () {
if [ "$first_try" ]; then
first_try=""
return 0
fi
if [ "$NONINTERACTIVE" ]; then
if [ ! "$first_ask" ]; then
return 1
else
first_ask=""
return 0
fi
fi
db_subst hw-detect/load_firmware FILES "$files"
if ! db_input high hw-detect/load_firmware; then
if [ ! "$first_ask" ]; then
exit 1;
else
first_ask=""
fi
fi
if ! db_go; then
exit 10 # back up
fi
db_get hw-detect/load_firmware
if [ "$RET" = true ]; then
return 0
else
echo "$files" | tr ' ' '\n' >> $DENIED
return 1
fi
}
list_deb_firmware () {
udpkg -c "$1" \
| grep '^\./lib/firmware/' \
| sed -e 's!^\./lib/firmware/!!' \
| grep -v '^$'
}
check_deb_arch () {
arch=$(udpkg -f "$1" | grep '^Architecture:' | sed -e 's/Architecture: *//')
[ "$arch" = all ] || [ "$arch" = "$(udpkg --print-architecture)" ]
}
# Remove non-accepted firmware package
remove_pkg() {
pkgname="$1"
# Remove all files listed in /var/lib/dpkg/info/$pkgname.md5sum
for file in $(cut -d" " -f 2- /var/lib/dpkg/info/$pkgname.md5sum) ; do
rm /$file
done
}
install_firmware_pkg () {
if echo "$1" | grep -q '\.deb$'; then
# cache deb for installation into /target later
mkdir -p /var/cache/firmware/
cp -aL "$1" /var/cache/firmware/ || true
filename="$(basename "$1")"
pkgname="$(echo $filename |cut -d_ -f1)"
udpkg --unpack "/var/cache/firmware/$filename"
if [ -f /var/lib/dpkg/info/$pkgname.preinst ] ; then
# Run preinst script to see if the firmware
# license is accepted Exit code of preinst
# decide if the package should be installed or
# not.
if /var/lib/dpkg/info/$pkgname.preinst ; then
:
else
remove_pkg "$pkgname"
rm "/var/cache/firmware/$filename"
fi
fi
else
udpkg --unpack "$1"
fi
}
# Try to load udebs (or debs) that contain the missing firmware.
# This does not use anna because debs can have arbitrary
# dependencies, which anna might try to install.
check_for_firmware() {
echo "$files" | sed -e 's/ /\n/g' >/tmp/grepfor
for filename in $@; do
if [ -f "$filename" ]; then
if check_deb_arch "$filename" && list_deb_firmware "$filename" | grep -qf /tmp/grepfor; then
log "installing firmware package $filename"
install_firmware_pkg "$filename" || true
fi
fi
done
rm -f /tmp/grepfor
}
while check_missing && ask_load_firmware; do
# first, check if needed firmware (u)debs are available on the
# PXE initrd or the installation CD.
if [ -d /firmware ]; then
check_for_firmware /firmware/*.deb /firmware/*.udeb
fi
if [ -d /cdrom/firmware ]; then
check_for_firmware /cdrom/firmware/*.deb /cdrom/firmware/*.udeb
fi
# second, look for loose firmware files on the media device.
if mountmedia; then
for file in $files; do
try_copy "$file"
done
umount /media || true
fi
# last, look for firmware (u)debs on the media device
if mountmedia driver; then
check_for_firmware /media/*.deb /media/*.udeb /media/*.ude /media/firmware/*.deb /media/firmware/*.udeb /media/firmware/*.ude
umount /media || true
fi
# remove and reload modules so they see the new firmware
# Sort to only reload a given module once if it asks for more
# than one firmware file (example iwlagn)
for module in $(echo $modules | tr " " "\n" | sort -u); do
if ! nic_is_configured $module; then
log "removing and loading kernel module $module"
modprobe -r $module || true
modprobe $module || true
fi
done
done
|