summaryrefslogtreecommitdiffstats
path: root/dracut/driver-updates.sh
blob: a07722ac060a1d90c4591fd55cb662e16e71c4ef (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
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
#!/bin/sh

# load the dracut libs
command -v getarg >/dev/null || . /lib/dracut-lib.sh
. /lib/url-lib.sh

# load the anaconda lib
. /lib/anaconda-lib.sh

# check if the DUD has the proper signature file
# and contains rpms for the given architecture
function dud_check()
{
    localdud=$1
    arch=$2

    [ -e "$localdud/rhdd3" -a -e "$localdud/rpms/$arch" ];
}

# copy the rpm repository to RAM so we can eject the media
function dud_copy()
{
    localdud=$1
    arch=$2
    dest=$3

    cp -ar "$localdud/rpms/$arch" "$dest"
}

#dud_contents <driverdisc> <arch> <kernel> <anaconda>
function dud_contents()
{
    local KERNEL=${3:-`uname -r`}
    local ANACONDA=${4:-"master"}
    local ARCH=${2:-`uname -m`}
    local localdud=$1

    "/bin/dud_list" -k "$KERNEL" -a "$ANACONDA" -d "$localdud"
}

# filter the driver update list so only the modules
# meant for instalation are in it
# TODO 7.1 there is no implementation of the rule mechanism atm.
function dud_rules()
{
    cat
}

# extract driver update rpms selected for installation
# the list is passed using standard input
function dud_extract_all()
{
    local repo=$1
    local dest=$2
    local rpm
    local arg
    local args
    local extract_args

    read rpm args
    while [ "x$rpm" != "x" ]; do
        echo "Extracting driver update rpm $rpm"
        extract_args=""
        for arg in $args; do
            extract_args="$extract_args --$arg"
        done

        mkdir -p "$dest"
        pushd "$dest"
        echo "/bin/dud_extract" $extract_args --rpm=$rpm --directory=.
        "/bin/dud_extract" $extract_args --rpm=$rpm --directory=.
        popd

        read rpm args
    done
}

# stdin -> input data
# dud_parse x|. (default value)
function dud_parse()
{
    local default=$1
    local DESCTMP

    read L
    while [ "x$L" != "x" ]; do
        SELECTED+=("$default")
        FILES+=("$L")
        read L
        MODULES+=("$L")
        read L
        FLAGS+=("$L")
        read L
        DESCTMP=""
        while [ "x$L" != "x---" ]; do
            DESCTMP="$DESCTMP\n$L"
            read L
        done
        DESCRIPTIONS+=("$DESCTMP")
        read L
    done
}

# dud_select <title line> <input data> [<default value> [noninteractive]]
# This is the (non)interactive part of module selection,
# the user can select drivers to install using simple text interface
# The list of selected modules gets printed out to the 3rd filedes
function dud_select()
{
    local MODULES=()
    local FILES=()
    local DESCRIPTIONS=()
    local FLAGS=()
    local SELECTED=()

    local DEFAULT=${3:-"."}
    local MODE=${4:-"interactive"}
    
    local L

    dud_parse $DEFAULT <$2

    if [ $MODE == "interactive" ]; then
        L="x"
    else
        L=""
    fi

    # TUI
    while [ "x$L" != "x" -a ${#MODULES[*]} -gt 0 ]; do
        # header
        clear
        echo $1
        echo

        for i in "${!FLAGS[@]}"; do
            echo $i ${SELECTED[$i]} ${MODULES[$i]} #${FLAGS[$i]}
        done

        echo
        echo -n "Please type a number of the package you want to select or unselect: "

        read L

        if [[ "$L" =~ ^[0-9]+$ ]]; then
            if [ ${SELECTED["$L"]} == "x" ]; then
                SELECTED[$L]="."
            elif [ ${SELECTED["$L"]} == "." ]; then
                SELECTED[$L]="x"
            fi
        fi
    done

    # print all selected drivers to the result descriptor (3)
    for i in "${!FLAGS[@]}"; do
        if [ ${SELECTED[$i]} == "x" ]; then
            echo ${FILES[$i]} ${FLAGS[$i]} 1>&3
        fi
    done
}

# process potential DUD for a given architecture
function driverupdatedisc()
{
    local device=$1
    local arch=$2
    local num=$3
    local DUD
    local MODE
    local DEFAULT

    echo "Checking Driver Update Disc $device..."
    DUD="/media/dud"

    mkdir -p "$DUD"
    mount $device "$DUD" || return
    dud_check "$DUD" $arch || umount /media/dud || return

    dud_copy "$DUD" $arch /tmp/DD-$num
    dud_contents /tmp/DD-$num >/tmp/dud_list.txt

    if [ -e "$DUD/rhdd3.rules" ]; then
        MODE="interactive"
        DEFAULT="."
    else
        MODE="noninteractive"
        DEFAULT="x"
    fi

    dud_select "Select DUP RPMs to install" /tmp/dud_list.txt $DEFAULT $MODE 3>/tmp/dud_extract.txt

    # extract the module files
    dud_extract_all /tmp/DD-$num /tmp/duds/DD-$num </tmp/dud_extract.txt

    # copy modules and firmwares to the proper directories
    # modules are in <kernel version>/{kernel,updates,extra}/* structure so
    # strip the kernel version and the first directory name of it
    mkdir -p "/lib/modules/$(uname -r)/updates/DD" "/lib/firmware/updates"
    cp -r /tmp/duds/DD-$num/lib/modules/*/*/* "/lib/modules/$(uname -r)/updates/DD"
    cp -r /tmp/duds/DD-$num/lib/firmware/* /lib/firmware/

    # copy binaries and libraries to / directory
    # TODO
    
    # save the list of extracted modules for later use in anaconda
    cat /tmp/dud_extract.txt >>/tmp/dud_extracted.txt

    # TODO add DD-X as a Yum repository

    # release the DUD
    umount "$DUD"

    # regenerate module lists
    depmod -a

    # get a list of modules that weren't loaded when this script started
    cut -f1 -d " " /proc/modules >/tmp/dud_state_current
    cat /tmp/dud_state /tmp/dud_state_current | sort | uniq -u >/tmp/dud_rmmod

    # iterative removal of package dependencies
    local REMOVING=1

    # remove all modules that were added by driver updates and can be removed
    while [ $REMOVING -gt 0 ]; do
        # this might be the last iteration
        REMOVING=0

        for mod in $(cat /tmp/dud_rmmod); do
            rmmod $mod 2>/dev/null
            if [ $? -eq 0 ]; then
                # is something was removed, there needs to be another iteration
                # to remove the modules that depended on this one
                echo "Module $mod removed"
                REMOVING=1
            fi
        done
    done

    # wait a bit to let the kernel and udev stabilize
    sleep 1

    # load modules (in their new version incarnation) again
    udevadm trigger
}

#
# The main flow of execution starts below this point
#


# save module state
cut -f1 -d " " /proc/modules >/tmp/dud_state

# get hw architecture
ARCH=$(uname -i)

# load all modules
udevadm trigger

# check all devices for the "oemdrv" label
DUDS=$(blkid -t LABEL=OEMDRV | cut -d: -f1)
NUM=0

# go through the detected devices
for dud in $DUDS; do
    driverupdatedisc $dud $ARCH $NUM
    NUM=$(expr $NUM + 1)
done

# manual driver selection
DD=$(getarg dd)

# TODO what if there are multiple dd= arguments?
if [ $? -eq 0 ]; then
    # if dd or dd=<nonsense>, show UI
    # else if dd=url, skip now, we will process it when network is ready
    # else skip UI steps and use the argument as path to the image

    # UI select device
    # UI select partition (if there are any)
    # UI browse directories (if rhdd3 file is present, skip following steps)
    # UI select driver update disc image
    driverupdatedisc $dud $ARCH $NUM # TODO skip mounting for directory based DUDs
    NUM=$(expr $NUM + 1)
fi