summaryrefslogtreecommitdiff
path: root/dot/local/bin/restart-linux-device
blob: e1cd41fa1dcec15b2603acbedfde7d0f9fca90e6 (plain)
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
#!/bin/bash

# Thanks, ericwoud
# https://ubuntuforums.org/showthread.php?t=2339439&page=2&p=13585921#post13585921

warn() { printf 'Error: %s\n' "$*" >&2; }

with_rescan_remove()
{
    local devname="$1" dev f hw_path rescan remove
    shift

    case "$devname" in
        */*)
            dev=/sys/class/$devname ;;
        *)
            for f in /sys/class/*/"$devname"; do
                dev=$f
                break
            done
            ;;
    esac
    [ "$dev" -a -d "$dev" ] || { warn "device not found: $devname"; return 1; }

    hw_path=$(dirname $(dirname $(realpath "$(dirname "$dev")/$(readlink "$dev")")))

    rescan=$(dirname "$hw_path")/rescan
    remove="$hw_path"/remove

    if [ ! -e "$rescan" -a -e "$hw_path"/../pci_bus ]; then
        local p=$(dirname "$hw_path")/
        rescan=$p/pci_bus/${p#*/pci}/rescan
    fi

    [ -e "$rescan" ] || { warn "sysfs special file not found: $rescan"; return 1; }
    [ -e "$remove" ] || { warn "sysfs special file not found: $remove"; return 1; }

    "$@" "$rescan" "$remove"
}

restart()
{
    local rescan="$1" remove="$2"
    printf 1 > "$remove" || { warn "kernel remove failed"; return 2; }
    printf 1 > "$rescan" || { warn "kernel rescan failed"; return 3; }

    for ((i=0; i<100; ++i)); do
        [ -e "$remove" ] && break
        sleep 0.05
    done
}

with_rescan_remove "$1" restart