привет всем!
моя безудержная страсть к ковырянию grub2 привела в итоге к мысли о создании некоего альтернативного конфигуратора для этого зверя, более простого и более понятного. почитав grub'овские скрипты, я понял, что черт голову сломит там, и за основу их брать стоит только в случае крайней нужды. но для начала о том, зачем вообще мне все это надо.
как я только что упомянул, скрипты grub2 довольно-таки хитры и запутаны. в связи с этим попытки освоить логику их работы часто оканчиваются крахом по причине недостаточности знаний (особенно в области регулярных выражений и использования sed, которого там предостаточно), а желание что-либо изменить/улучшить может обернуться либо крахом, либо и вовсе поломкой скрипта. опять же, все из-за запутанности и отсутствия хотя бы минимальных комментариев.
ко всему прочему не могу не отметить, что стандартная система конфигурации grub2 сильно децентрализоована и представляет из себя эдакую раскиданную по системе паутину. многим известные "/etc/default/grub" и скрипты из "/etc/grub.d/" - это лишь часть сложного механизма. из-за этого полноценный перенос grub2 на другую систему при отсутствии в таковой родной поддержки весьма проблематичен. также затруднено создание загрузочных носителей на базе grub2 - приходится либо писать конфиг вручную, либо "колдовать", монтируя одни папки вместо других и прибегая к хитростям.
в получаемом с помощью скриптов конфиге много странных вещей непонятного назначения, без которых, однако, все работает ничуть не хуже. в некоторых случаях достаточно простенького конфига в несколько строчек, а не того нагромождения, что создает стандартная система.
еще один важный момент, замеченный многими при переходе на grub2 - разбросанность настроек. в отличие от старых версий, где если не все, то хотя бы основные настройки хранились в одном файле, в grub2 настройки раскиданы по разным местам. не всем удобно править кучу разных файлов, а новичков (для которых ручная правка конфигов как таковая является делом непривычным и непонятным) и вовсе должно пугать такое положение дел.
по технической части отдельного внимания заслуживает странная система маппинга дисков (что преобразовывает "sda5" в "(hd0,5)"), которая не всегда ведет себя адекватно, а порой доходит до того, что отображает лишние жесткие диски. несмотря на это, она до сих используется, хотя сами разработчики признают, что на нее не стоит слишком надеяться.
ну и наконец, исторический момент - закостенелость проекта. если сравнить текущее состояние grub2 с версией полуторалетней давности - по сути мало что изменилось. конечно, что-то делается, но за время существования проекта можно было собрать результаты тестирований, опросить пользователей и т.д. конечно, у разработчиков могут быть свои проблемы - нехватка времени и ресурсов и все прочее, но факт есть факт.
ко всему вышесказанному у многих, думаю, найдется что добавить, исходя из личного опыта работы с grub2. исходя из этого я и решил внести свой посильный вклад в изменение данного проекта. на техническую часть у меня, конечно, не хватит ни сил, ни желания, а вот написать скрипт конфигурации - это реально. главное раскрутить это дело, а желающие глядишь и найдутся.
ну вот, все вышеизложенное можно было не читать, это типа предисловие.
в чем суть идеи? вкратце - суть в том, чтобы создать человеческим языком написанный скрипт конфигурации и при этом свести все настройки и операции в один файл, который одновременно является и скриптом и своего рода конфигом.
в чем преимещуства такого метода? подобная система легко портируема: достаточно будет установить grub2 на нуный диск и скопировать туда один-единственный скрипт, с помощью которого и будет вестить вся дальнейшая настройка. такой подход более понятен: согласитесь, куда проще настроить все в одном файле, чем бегать по разным местам. куда лучше (как для пользователя, так и для системы), когда для изменения какой-либо опции пользователю не нужно куда-то в середину скрипта (рискуя стереть там какую-нибудь запятую), а поменять отдельно вынесенный параметр.
вот такая прекрасная идея получается... а что в реале? а в реале прогресс есть!
на текущий момент уже работает настройка основных параметров (настройки меню, оформление) и, что более интересно, система создания меню для текущей linux-системы (т.е. уже готовая замена 10_linux). естественно, смысл не в том, чтобы просто переделать, а в том, чтобы усовершенствовать. итак, работающие на текущий момент фичи:
- настраиваемая сортировка списка ядер. можно выбрать сортировку по номеру версиии или по дате изменения. смысл: если раньше самосборное ядро с версией "2.6.33-rc5-src01" шло бы ниже ядра "2.6.33-020633-rc5" (т.к. применялась сортировка только по имени), то теперь можно выбрать сортировку по дате, и более новые ядра будут всегда вверху в списке
- проверка на наличие initrd-образа к ядру. по умолчанию если найден файл ядра (vmlinuz...), но не найден образ initrd к нему - такое ядро не будет добавлено в список загрузки. однако же, есть специальный режим, позволяющий использование монолитных ядер без iniitd
- режим "только последнее ядро": в список загрузки будет включаться только самое новое ядро. можно выбрать использование новейшего ядра (при обновлении ядра меню будет обновляться под него) или же использование текущего ядра (например, если вы используете самосборное ядро, а стандартные вас не интересуют)
- возможность задания выходного файла правкой одного-единственного параметра. хотите - сохраняйте полученный конфиг как grub.cfg, хотите - еще куда угодно. можно создать несколько по-разному настроенных скриптов и делать разные конфиги.
- проверка фонового изображения на соответствие установленному разрешению экрана (опционально)
- оригинальная реализация "recovery mode". для каждого ядра можно создать только один пункт меню. при обычном выборе этого пункта система будет грузиться с нормальнымии параметрами. если же при этом удерживать заданную клавишу (ctrl, alt или shift) - система загрузится в reovery mode (опционально)
- разумный минимализм в создании конфига (по крайней мере, стремление к таковому). необходимые компоненты подгружаются по необходимости, но получаемый конфиг вполне читабелен, нет нагромождения
- более-менее информативный вывод на консоль сообщений о происходящих действиях.
вывод: на ubuntu все работает. так что для тех, у кого одна ос - уже в целом готово к использованию.
да, интересный факт: провел текст на скорость с участием стандартного update-grub и получившегося скрипта. вот что вышло (по показаниям утилиты time):
- update-grub (включены скрипты 00, 05 и 10):
real 0m1.476s
- скрипт:
real 0m0.188s
как видим, разница заметна. при одинаковом результате на выходе.
короче, вот что я скажу: ежели кто заинтересовался данным прожектом - пишите, спрашивайте, присоединяйтесь. тем более, нужна будет поддержка по части windows. кроме того, возможно, планируется поддержка FreeBSD (если удастся разобраться как там что). в скором времени будет поддержка загрузки "bzImage + initramfs".
да, sudo внутри пока что оставлены на пробной стадии. впоследствии будут убраны, скрипт будет полноценно работать от рута (+ планирую добавить еще что-то типа "you must run this as root" в случае попытки запуска без нужных прав). но это позже. сейчас главное - тестирование, допилинг и... работа над загрузкой виндовоза!
кстати, в планах есть идея с ограничением списка разделов, на которых будем искать другие ос. чтоб не шарил почем зря где попало.
знатоки и энтузиасты баша (и sed'а!) горячо приветствуются!!
сабж ниже
#! /bin/sh
#### CONFIG SECTION ####
## GENERIC OPTIONS
# where to save resulting config file
output_file="tmp/grub.test"
# default menuentry. can be number of entry
# in config (starting with 0) or exact name
default="0"
# how long to display menu before booting
# default entry. set "-1" for infinite delay
timeout="2"
# colors for text mode. first color is for
# font, second is for background.
# generic_color - usual menuentry, all texts
# generic_highlight - selected menuentry
generic_color="white/black"
generic_highlight="black/white"
# colors for graphic mode. quite the same,
# but black as background means transparency
# graphic_default_text is for help/welcome
# texts in top/bottom and for console
graphic_default_text="black/black"
graphic_color="black/black"
graphic_highlight="black/light-gray"
# background image for menu
background="/usr/share/images/grub/sky.png"
# set screen rsolution: WxH[xDepth]
# you can use multiple modes, separated
# by comma (no spaces!)
gfxmode="640x480x32,640x480x24,640x480"
# if enabled, only image with
# size = screen resolution (see above)
# will be accepted. value: true/false
bg_hard_check=true
# choose font to use in menu.
# value should be absolute path
font="/usr/share/grub/unicode.pf2"
## END OF GENERIC OPTIONS
## LINUX OPTIONS
# how to sort your vmlinuz (kernel) files:
# by name or by date. values: "date", "name"
sort_type="date"
# kernel options.
# use "quiet splash" for graphical boot
kernel_params="quiet splash"
# this option allow you to hide # "recovery"
# entries. recovery mode will be # avaliable if
# you select the menuentry while # holding
# specified key.
# allowed keys are "ctrl", "shift" and "alt"
# comment or change to "off" to disable
hidden_recovery_key="ctrl"
# this option allows using monolith kernels
# without initrd. if enabled, kernels can be
# used without initrd, otherwise kernel without
# initrd will be skipped. values: true/false
allow_monolith=false
# add only one kernel. can be last installed
# kernel or currently running one.
# values: "running" and "latest"
# comment or change to "off" to disable
last_kernel_only="off"
## END OF LINUX OPTIONS
#### END OF CONFIG SECTION ####
################################
## don't touch anything below ##
## this text unless you know ##
## what are you doing!! ##
################################
################################
## GENERIC SECTION ##
################################
echo "=> generating configuration file: $output_file" >&2
# finding system root
sys_root=`mount | grep " / " | grep -o "/dev/sd[a-z][0-9]"`
# determining root UUID and fs type
root_uuid=`sudo blkid $sys_root -o value | head -n 1`
root_fs=`sudo blkid $sys_root -o value | tail -n 1`
# checking for separate /boot and determining its params
if grep "/boot" /etc/fstab > /dev/null ; then
boot_prefix=""
boot_drive=`mount | grep "/boot" | grep -o "/dev/sd[a-z][0-9]"`
boot_uuid=`sudo blkid $boot_drive -o value | head -n 1`
boot_fs=`sudo blkid $boot_drive -o value | tail -n 1`
else
boot_prefix="/boot"
boot_uuid=$root_uuid
boot_fs=$root_fs
fi # separate boot
generic() {
echo "=> configuring default options" >&2
cat << EOF
set default="$default"
set timeout="$timeout"
EOF
case $boot_fs in
ext*) fs_mod="ext2" ;;
reiserfs) fs_mod="reiserfs" ;;
xfs) fs_mod="xfs" ;;
jfs) fs_mod="jfs" ;;
*) fs_mod="$boot_fs" ;;
esac
# loading fs module and customizing graphics
cat << EOF
insmod $fs_mod
set gfxmode="$gfxmode"
EOF
if [ -s $font ] ; then # if font file exists
font_place="/$(ls $font | grep -o "$(ls /)" | head -n 1)"
if grep "$font_place" /etc/fstab > /dev/null ; then # if font is on non-root partiotion
# making font path relative to partition
font_dev=`mount | grep "$font_place" | grep -o "/dev/sd[a-z][0-9]"`
font_place_uuid=`sudo blkid $font_dev -o value | head -n 1`
sed_rule=`echo "s|$font_place||"`
font=`echo "$font" | sed $sed_rule`
else
font_place_uuid=$root_uuid
fi # separate drive for font_place
if [ "$font_place_uuid" != "$boot_uuid" ] ; then
# if font is on another partition than /boot, change grub's root
echo "search -u $font_place_uuid -s"
fi # uuid differs from boot's
#l loading graphics
cat << EOF
if loadfont $font ; then
insmod gfxterm
insmod vbe
if terminal_output gfxterm ; then true ; else terminal gfxterm; fi
set color_normal=$graphic_default_text
set menu_color_normal=$graphic_color
set menu_color_highlight=$graphic_highlight
else
set color_normal=$generic_color
set color_highlight=$generic_highlight
fi
EOF
fi # font exists
# checking and setting background
if [ -s $background ] ; then
bg_size=`file $background | grep -o "[0-9]* x [0-9]*" | sed 's| ||g'`
sizetest=`echo "$gfxmode" | grep -Eo "^[^,]*" | grep "$bg_size"`
if [ $sizetest ] ; then
bg_ok=true
elif [ "$bg_hard_check" = true ] ; then
bg_ok=false
echo "=> WARNING: background image $(basename $background) seems to have incorrect size or format" >&2
else bg_ok=false
fi # sizetest
if [ "$bg_ok" = true ] ; then
bg_place="/$(ls $background | grep -o "$(ls /)" | head -n 1)"
if grep "$bg_place" /etc/fstab > /dev/null ; then # if bg is on non-root partiotion
# making bg path relative to partition
bg_dev=`mount | grep "$bg_place" | grep -o "/dev/sd[a-z][0-9]"`
bg_place_uuid=`sudo blkid $bg_dev -o value | head -n 1`
sed_rule=`echo "s|$bg_place||"`
background=`echo "$background" | sed $sed_rule`
else
bg_place_uuid=$root_uuid
fi # separate drive for bg_place
if [ "$bg_place_uuid" != "$boot_uuid" ] ; then
# if bg is on another partition than /boot, change grub's root
echo "search -u $bg_place_uuid -s"
fi # uuid differs from boot's
case `basename $background` in
*.png) echo "insmod png" ;;
*.tga) echo "insmod tga" ;;
esac
echo "background_image $background"
echo "=> found background image: $(basename $background), $bg_size" >&2
fi # bg_ok
fi # bg file exists
} # generic function
generic 1> $output_file
################################
## LINUX SECTION ##
################################
linux() {
# setting hidden recovery state
case $hidden_recovery_key in
ctrl) hidden_recov=true;;
alt) hidden_recov=true;;
shift) hidden_recov=true;;
*) hidden_recov=false;;
esac
# looking for installed kernels
case $sort_type in
name) sort_switch="-r";;
date) sort_switch="-t";;
*) sort_switch="-r";;
esac
case $last_kernel_only in
latest) kernel_list=`ls -1t /boot/vmlin* | head -n 1` ;;
running) kernel_list="/boot/vmlin*`uname -r`" ;;
*) kernel_list=`ls -1 $sort_switch /boot/vmlin*`
esac
for name in $kernel_list ; do
# determinig kernel version
kernel_ver=`echo $name | sed -e 's|^[^0-9]*||g'`
vmlinuz_file=`basename $(ls /boot/vmlinuz*$kernel_ver)*`
linux_line="linux $boot_prefix/$vmlinuz_file root=UUID=$root_uuid"
echo "=> found Linux kernel $kernel_ver" >&2
# checking for initrd
if [ -s /boot/init*$kernel_ver* ] ; then
initrd_file=`basename $(ls /boot/init*$kernel_ver)*`
initrd_line="initrd $boot_prefix/$initrd_file"
good_kernel=true
elif [ "$allow_monolith" = true ] ; then
initrd_line="#this kernel seems to be monolith, no initrd is used"
echo "=> kernel $kernel_ver will be used as monolith, without initrd" >&2
good_kernel=true
else
echo "=> ERROR: no initrd found for kernel $kernel_ver, skipping it" >&2
good_kernel="false"
fi # initrd check
if [ $good_kernel = true ] ; then
# making menuentry
title="$(lsb_release -ds) @ $kernel_ver"
case $hidden_recov in
false)
cat << EOF
menuentry "$title" {
search -u $boot_uuid -s
$linux_line $kernel_params
$initrd_line
}
menuentry "$title (recovery)" {
search -u $boot_uuid -s
$linux_line single
$initrd_line
}
EOF
;;
true)
cat << EOF
menuentry "$title" {
search -u $boot_uuid -s
if keystatus --$hidden_recovery_key ; then
$linux_line single
else
$linux_line $kernel_params
fi
$initrd_line
}
EOF
;;
esac # hidden_recov true/false
fi # good_kernel
done # for <every kernel> do ....
} # linux function
linux 1>> $output_file
echo "=> done" >&2
ЗЫ. если доберусь сегодня до дебиана - погоняю на нем...