Use ${PROG} instead of $0 in sysresccd-usbstick
[systemrescuecd.git] / portage-overlay / sys-apps / sysresccd-scripts / files / sysresccd-usbstick
1 #!/bin/bash
2 # Project page: http://www.sysresccd.org/
3 # (C) 2008 Francois Dupoux
4 # This scipt is available under the GPL-2 license
5
6 # it may also be interesting to reset the MBR
7 # dd if=/usr/lib/syslinux/mbr.bin of=/dev/sdf
8
9 ## HELP AND BASIC ARGUMENT PROCESSING
10 #####################################
11
12 PROG="${0}"
13 ISODIR="/livemnt/boot"
14 USBDIR="/mnt/usbstick"
15 SYSLINUX="sylinux"
16 MINSIZEMB=300
17 cdfiles=('sysrcd.dat' 'sysrcd.md5' 'version' 'isolinux/initram.igz' 
18         'isolinux/rescuecd' 'isolinux/rescue64' 'isolinux/f1boot.msg'
19         'isolinux/isolinux.bin' 'isolinux/isolinux.cfg')
20
21 usage()
22 {
23         cat <<EOF
24 sysresccd-usbstick: SystemRescueCd installation script for USB-sticks
25 Syntax:             sysresccd-usbstick <command> ...
26
27 Please, read the manual for help about how to use this script.
28 http://www.sysresccd.org/Online-Manual-EN
29 This script must be executed outside of the chroot environment.
30
31 Commands (execute in that order):
32  1) listdev               Show the list of removable media
33  2) writembr <devname>    Recreate the MBR + partition table on the stick
34  3) format <partname>     Format the USB-stick device (overwrites its data)
35  4) copyfiles <partname>  Copy all the files from the cdrom to the USB-stick
36  5) syslinux <partname>   Make the device bootable
37
38  -h|--help                Display this screen
39  -i ISODIR                Set the mount directory for the stock SysRescCD iso ($ISODIR)
40  -u USBDIR                Set the directory for mounting the USB stick ($USBDIR)
41  -m SYSLINUX              Set the syslinux program ($SYSLINUX)
42  -s MINSIZEMB             Minimal size required for SysRescCD in mega-bytes
43  dialog                   Dialog to make SystemRescueCD-USB-Sticks
44
45 Distributed under the GNU Public License version 2 - http://www.sysresccd.org
46 EOF
47 }
48
49 ## MISC FUNCTIONS: Many utilities functions
50 ###########################################
51
52 # show the error message ($1 = first line of the message)
53 help_readman()
54 {
55         echo "$1"
56         echo "Please, read the manual for more help about this script"
57         echo "Web: http://www.sysresccd.org"
58         exit 1
59 }
60
61 ## ERROR HANDLING
62 #####################################
63
64 die()
65 {
66         if [ -n "$1" ]
67         then
68                 echo "${PROG}: error: $1"
69         else
70                 echo "${PROG}: aborting."
71         fi
72         exit 1
73 }
74
75 ## MISC FUNCTIONS: Many utilities functions
76 ###########################################
77
78 # check that there is one partition and one only on block-device $1
79 find_first_partition()
80 {
81         devname="$1"
82         if [ -z "${devname}" ] || [ ! -d "/sys/block/$(basename ${devname})" ]
83         then
84                 die "${devname} is not a valid device name"
85         fi
86         
87         partcnt=0
88         firstpart=0
89         for i in $(seq 1 4)
90         do
91                 partname="${devname}${i}"
92                 if [ -b "${partname}" ]
93                 then
94                         [ "${firstpart}" = '0' ] && firstpart="$i"
95                         partcnt=$((partcnt+1))
96                 fi
97         done
98         
99         if [ "${partcnt}" = '1' ]
100         then
101                 return ${partcnt}
102         else
103                 return 0
104         fi
105 }
106
107 # check $1 is a valid partition name
108 check_valid_partname()
109 {
110         if [ -z "${partname}" ]
111         then
112                 die "you have to provide a valid partition device-name as argument of this command"
113         fi
114
115         if [ -z "${partname}" ] || [ ! -b "${partname}" ]
116         then
117                 die "${partname} is not a valid partition name"
118         fi
119         
120         if ! echo "${partname}" | grep -qE '^/dev/[a-z]*[1-4]+$'
121         then
122                 die "device [${partname}] is not a valid partition. Expect something like [/dev/sdf1]"
123         fi
124
125         if is_dev_mounted "${partname}"
126         then
127                 die "${partname} is already mounted, cannot continue"
128         fi
129         
130         return 0
131 }
132
133 # check $1 is a valid block device name
134 check_valid_blkdevname()
135 {
136         if [ -z "${devname}" ]
137         then
138                 die "you have to provide a valid device name as argument of this command"
139         fi
140         
141         if [ ! -b "${devname}" ] || [ ! -d "/sys/block/$(basename ${devname})" ]
142         then
143                 die "${devname} is not a valid device name"
144         fi
145         
146         if is_dev_mounted "${devname}"
147         then
148                 die "${devname} is already mounted, cannot continue"
149         fi
150         
151         return 0
152 }
153
154 check_sysresccd_files()
155 {
156         rootdir="$1"
157         [ -z "${rootdir}" ] && rootdir="${ISODIR}"
158         for curfile in ${cdfiles[*]}
159         do
160                 curcheck="${rootdir}/${curfile}"
161                 if [ ! -f ${curcheck} ]
162                 then
163                         die "Cannot find ${curcheck}, cannot continue"
164                 fi
165         done
166         return 0
167 }
168
169 # returns 0 if the device is big enough
170 check_sizeof_dev()
171 {
172         devname="$1"
173
174         if [ -z "${devname}" ]
175         then
176                 die "check_sizeof_dev(): devname is empty"
177         fi
178
179         if [ -z "$(which blockdev)" ]
180         then
181                 echo "blockdev not found, assuming the size is ok"
182                 return 0
183         fi
184         
185         secsizeofdev="$(blockdev --getsz ${devname})"
186         mbsizeofdev="$((secsizeofdev/2048))"
187         if [ "${mbsizeofdev}" -lt "${MINSIZEMB}" ]
188         then
189                 die "The device [${devname}] is only ${mbsizeofdev} MB. It is too small to copy all the files, an USB-stick of at least ${MINSIZEMB}MB is recommended"
190         else
191                 echo "The device [${devname}] seems to be big enough: ${mbsizeofdev} MB."
192                 return 0
193         fi
194 }
195
196 # say how much freespace there is on a mounted device
197 check_disk_freespace()
198 {
199         freespace=$(\df -m -P ${1} | grep " ${1}$" | tail -n 1 | awk '{print $4}')
200         echo "Free space on ${1} is ${freespace}MB"
201         if [ "${freespace}" -lt "${MINSIZEMB}" ]
202         then
203                 die "There is not enough free space on the USB-stick to copy the SystemRescuecd files."
204         fi
205         return 0
206 }
207
208 # check that device $1 is an USB-stick
209 is_dev_usb_stick()
210 {
211         curdev="$1"
212         
213         remfile="/sys/block/${curdev}/removable"
214         vendor="$(cat /sys/block/${curdev}/device/vendor 2>/dev/null)"
215         model="$(cat /sys/block/${curdev}/device/model 2>/dev/null)"
216         if [ -f "${remfile}" ] && cat ${remfile} 2>/dev/null | grep -qF '1' \
217                 && cat /sys/block/${curdev}/device/uevent 2>/dev/null | grep -qF 'DRIVER=sd'
218         then
219                 return 0
220         else
221                 return 1
222         fi
223 }
224
225 do_writembr()
226 {
227         devname="$1"
228         shortname="$(echo ${devname} | sed -e 's!/dev/!!g')"
229         
230         check_valid_blkdevname "${devname}"
231         if ! is_dev_usb_stick "${shortname}"
232         then
233                 die "Device [${devname}] does not seem to be an usb-stick. Cannot continue."
234         fi
235         
236         check_sizeof_dev "${devname}"
237         
238         if [ -z "$(which install-mbr)" ] || [ -z "$(which parted)" ]
239         then
240                 die "install-mbr and parted must be installed, check these programs first."
241         fi
242         
243         cmd="install-mbr ${devname} --force"
244         echo "--> ${cmd}"
245         if ! ${cmd}
246         then
247                 die "${cmd} --> failed"
248         fi
249         
250         cmd="parted -s ${devname} mklabel msdos"
251         echo "--> ${cmd}"
252         if ! ${cmd} 2>/dev/null
253         then
254                 die "${cmd} --> failed"
255         fi
256         
257         cmd="parted -s ${devname} mkpartfs primary fat32 0 100%"
258         echo "--> ${cmd}"
259         if ! ${cmd} 2>/dev/null
260         then
261                 die "${cmd} --> failed"
262         fi
263         
264         cmd="parted -s ${devname} set 1 boot on"
265         echo "--> ${cmd}"
266         if ! ${cmd} 2>/dev/null
267         then
268                 die "${cmd} --> failed"
269         fi
270 }
271
272 do_format()
273 {
274         partname="$1"
275         check_valid_partname "${partname}"
276
277         check_sizeof_dev "${partname}"
278
279         if [ -z "$(which mkfs.vfat)" ]
280         then
281                 die "mkfs.vfat not found on your system, please install dosfstools first."
282         fi
283         
284         if mkfs.vfat -F 32 -n SYSRESC ${partname}
285         then
286                 echo "Partition ${partname} has been successfully formatted"
287                 return 0
288         else
289                 echo "Partition ${partname} cannot be formatted"
290                 return 1
291         fi
292 }
293
294 do_copyfiles()
295 {
296         partname="$1"
297         check_valid_partname "${partname}"
298         
299         # check the important files are available in ${ISODIR}
300         check_sysresccd_files "${ISODIR}"
301         
302         check_sizeof_dev "${partname}"
303         
304         mkdir -p ${USBDIR} 2>/dev/null
305         if ! mount -t vfat ${partname} ${USBDIR}
306         then
307                 die "cannot mount ${partname} on ${USBDIR}"
308         fi
309         echo "${partname} successfully mounted on ${USBDIR}"
310         
311         check_disk_freespace "${USBDIR}"
312         
313         if cp -r --remove-destination ${ISODIR}/* ${USBDIR}/ && sync
314         then
315                 echo "Files have been successfully copied to ${partname}"
316         else
317                 echo "Cannot copy files to ${partname}"
318         fi
319         
320         for curfile in '${USBDIR}/isolinux/isolinux.cfg'
321         do
322                 if [ ! -f "${curfile}" ]
323                 then
324                         umount ${USBDIR}
325                         die "${curfile} not found, cannot continue"
326                 fi
327         done
328         
329         # check the important files have been copied
330         check_sysresccd_files "${USBDIR}"
331         
332         # move isolinux files to syslinux files
333         rm -rf ${USBDIR}/syslinux
334         if ! mv ${USBDIR}/isolinux/isolinux.cfg ${USBDIR}/isolinux/syslinux.cfg \
335                 || ! mv ${USBDIR}/isolinux ${USBDIR}/syslinux
336         then
337                 umount ${USBDIR}
338                 die "cannot move isolinux to syslinux, failed"
339         fi
340         
341         # remove the last lines which produces error messages 'bad keyword' with syslinux
342         sed -i -e '/label disk[1-2]$/d' -e '/label floppy$/d' -e '/label nextboot$/d' -e '/localboot/d' ${USBDIR}/syslinux/syslinux.cfg
343         
344         # add scandelay option which allows the usb devices to be detected
345         sed -i -e 's!initrd=initram.igz!initrd=initram.igz scandelay=5!g' ${USBDIR}/syslinux/syslinux.cfg
346         
347         umount ${USBDIR}
348 }
349
350 do_syslinux()
351 {
352         partname="$1"
353         check_valid_partname "${partname}"
354         
355         if [ -z "$(which ${SYSLINUX})" ]
356         then
357                 die "${SYSLINUX} not found on your system, please install syslinux first."
358         fi
359         
360         if ${SYSLINUX} ${partname} && sync
361         then
362                 echo "${SYSLINUX} has successfully prepared ${partname}"
363         else
364                 echo "${SYSLINUX} failed to prepare ${partname}"
365         fi
366 }
367
368 is_dev_mounted()
369 {
370         curdev="$1"
371         
372         if cat /proc/mounts | grep -q "^${curdev}"
373         then
374                 return 0
375         else
376                 return 1
377         fi
378 }
379
380 do_dialog()
381 {
382         lwselection="`mktemp /tmp/lwselection.XXXX`"
383         selection='dialog --backtitle "Select USB-Stick" --checklist "Select USB-Stick" 20 61 5'
384         devcnt=0
385         for curpath in /sys/block/*
386         do
387                 curdev="$(basename ${curpath})"
388                 devname="/dev/${curdev}"
389                 if is_dev_usb_stick ${curdev}
390                 then
391                         if [ -n "$(which blockdev)" ]
392                         then
393                                 secsizeofdev="$(blockdev --getsz /dev/${curdev})"
394                                 mbsizeofdev="$((secsizeofdev/2048))"
395                                 sizemsg=" and size=${mbsizeofdev}MB"
396                         fi      
397                         echo "Device [${devname}] detected as [${vendor} ${model}] is removable${sizemsg}"
398                         if is_dev_mounted "${devname}"
399                         then
400                                 echo "Device [${devname}] is mounted"
401                         else
402                                 echo "Device [${devname}] is not mounted"
403                         fi
404                         find_first_partition ${devname}
405                         firstpart="$?"
406                         if [ "${firstpart}" != '0' ]
407                         then
408                                 echo "Device [${devname}] has one partition: ${devname}${firstpart}"
409                                 selection="$selection \"${devname}\" \"\" off"
410                         else
411                                 echo "Cannot identify which partition to use on ${devname}"
412                         fi
413                         devcnt=$((devcnt+1))
414                 fi
415         done
416         if [ "${devcnt}" = '0' ]
417         then
418                 echo "No USB-stick has been detected."
419         else
420                 eval $selection 2>$lwselection
421                         if test -s $lwselection; then
422                                 for devname2 in `cat $lwselection  | tr -d \" | sort`; do
423                                 do_writembr ${devname2}
424                                 sleep 5
425                                 find_first_partition ${devname2}
426                                 devname2="${devname2}$?"
427                                 do_format ${devname2}
428                                 do_copyfiles ${devname2}
429                                 do_syslinux ${devname2}
430
431                                 done
432                         fi
433         fi
434         rm -f $lwselection
435 }
436
437 do_listdev()
438 {
439         devcnt=0
440         for curpath in /sys/block/*
441         do
442                 curdev="$(basename ${curpath})"
443                 devname="/dev/${curdev}"
444                 if is_dev_usb_stick ${curdev}
445                 then
446                         if [ -n "$(which blockdev)" ]
447                         then
448                                 secsizeofdev="$(blockdev --getsz /dev/${curdev})"
449                                 mbsizeofdev="$((secsizeofdev/2048))"
450                                 sizemsg=" and size=${mbsizeofdev}MB"
451                         fi      
452                         echo "Device [${devname}] detected as [${vendor} ${model}] is removable${sizemsg}"
453                         if is_dev_mounted "${devname}"
454                         then
455                                 echo "Device [${devname}] is mounted"
456                         else
457                                 echo "Device [${devname}] is not mounted"
458                         fi
459                         find_first_partition ${devname}
460                         firstpart="$?"
461                         if [ "${firstpart}" != '0' ]
462                         then
463                                 echo "Device [${devname}] has one partition: ${devname}${firstpart}"
464                         else
465                                 echo "Cannot identify which partition to use on ${devname}"
466                         fi
467                         devcnt=$((devcnt+1))
468                 fi
469         done
470         if [ "${devcnt}" = '0' ]
471         then
472                 echo "No USB-stick has been detected."
473         fi
474 }
475
476 ## MAIN SHELL FUNCTION
477 ########################################################
478
479 if [ $# -eq 0 ]    # Script invoked with no command-line args?
480 then
481         usage
482         exit 1
483 fi
484
485 while getopts ":ui:r:w:m:" Option
486 do
487         case $Option in
488                 i ) ISODIR="$OPTARG";;
489                 r ) ROOTDIR="$OPTARG";;
490                 w ) WORKDIR="$OPTARG";;
491                 * ) usage; exit 1;;  # Default, handles -h
492         esac
493 done
494 shift $(($OPTIND - 1))
495
496 COMMAND="${1}"
497 shift
498
499 if [ "$(whoami)" != "root" ]
500 then
501         help_readman "${PROG}: This script requires root privileges to operate."
502 fi
503
504 if ! cat /proc/mounts | awk '{print $2}' | grep -q -F '/memory'
505 then
506         help_readman "${PROG}: This script must be executed from SystemRescueCd"
507 fi
508
509 case "${COMMAND}" in
510         listdev)
511                 do_listdev
512                 ;;
513         writembr)
514                 do_writembr "$@"
515                 ;;
516         format)
517                 do_format "$@"
518                 ;;
519         copyfiles)
520                 do_copyfiles "$@"
521                 ;;
522         syslinux)
523                 do_syslinux "$@"
524                 ;;
525         dialog)
526                 do_dialog "$@"
527                 ;;
528         *)
529                 usage 
530                 exit 1
531                 ;;
532 esac
533 exit 0