#!/bin/sh
# /sbin/installkernel
# by Spina <spina80@gmail.com>
# Ultimo aggiornamento: 26/04/2011

# Questo script  inteso essere lanciato nella directory dei sorgenti del kernel
# tramite il comando:
#
#           make install

# Crea dei pacchetti Slackware per:
# - L'immagine del  kernel (chiamato kernel-image)
# - I moduli del kernel    (chiamato kernel-modules)
# - Gli headers del kernel (chiamato kernel-headers)
# quindi li installa sulla macchina (tramite installpkg) e lancia lilo.
# Il nuovo kernel sar installato nella directory /boot ed avr un link a se stesso chiamato
# vmlinuz (sempre nella directory /boot.
# Se esistevano precedentemente altri pacchetti Slackware con lo stesso nome, li disinstalla.
# I pacchetti creati vengono copiati nella directory $DEST_DIR

# Parametri (i parametri sono passati allo script dal "make install"):
# $1 = Versione del kernel compilato
# $2 = Path del kernel compilato

# Assicuriamoci che sia il root a lanciare lo script
if [ `id -u` -ne 0 ]; then
    echo "Only root can do it!";
    exit 1;
fi

# Inizializzazione variabili necessarie
TMP_DIR=/tmp                              # Directory temporanea
LOG_FILE=${TMP_DIR}/installkernel.log     # File del log
REP_LOG=/var/log/packages                 # Directory in cui si trova l'elenco dei pacchetti installati

KERNEL_PACK=${TMP_DIR}/package-kernel     # Directory per la creazione del pacchetto kernel-image
MODULES_PACK=${TMP_DIR}/package-modules   # Directory per la creazione del pacchetto kernel-modules
HEADERS_PACK=${TMP_DIR}/package-headers   # Directory per la creazione del pacchetto kernel-headers

ROOT_DIR=/usr/local/etc/installkernel     # Root directory degli slack-desc e doinst.sh per di ogni pacchetto
KERNEL_DIR=${ROOT_DIR}/package-kernel     # slack-desc e doinst.sh per il pacchetto kernel-image
MODULES_DIR=${ROOT_DIR}/package-modules   # slack-desc e doinst.sh per il pacchetto kernel-modules
HEADERS_DIR=${ROOT_DIR}/package-headers   # slack-desc e doinst.sh per il pacchetto kernel-headers

# Directory nella quale verranno copiati i pacchetti creati
DEST_DIR=/home/spina/software_linux/kernel/

# Sanity check. Tutte le directory necessarie devono esistere.
if [ ! \( -d $TMP_DIR -a -d $KERNEL_DIR -a -d $MODULES_DIR  -a -d $HEADERS_DIR -a "$DEST_DIR" \)  ]; then
    echo -e "\nLe seguenti directory sono tutte fondamentali:"\
"\n\t-> ${TMP_DIR}\n\t-> ${KERNEL_DIR}\n\t-> ${MODULES_DIR}\n\t ->${HEADERS_DIR}\n"\
"\t -> $DEST_DIR";
    exit 1;
fi

CWD=`pwd`

KERNEL_VERSION=$1 # Versione del kernel da installare
KERNEL_IMAGE=$2   # Path del kernel compilato tramite "make"

HOSTNAME=`hostname`  # Nome dell'host

# Estensione da aggiungere ai pacchetti creati
KERNEL_EXT=-${KERNEL_VERSION}-`arch`             # Versione del kernel e architettura
if grep -q "CONFIG_SMP=y" .config; then
    KERNEL_EXT=${KERNEL_EXT}_SMP_${HOSTNAME}-1     # Se  SMP e nome dell'host
else
    KERNEL_EXT=${KERNEL_EXT}_${HOSTNAME}-1         # In ogni caso il nome dell'host
fi

# Crea un pacchetto tramite makepkg e lo installa tramite installpkg, successivamente
# sposta il pacchetto nella directory $DEST_DIR
# Usata da:
# - _make_kernel_package
# - _make_modules_package
# - _make_headers_package
# Parametri:
# $1 = Directory root per il pacchetto da creare
# $2 = Directory da cui copiare lo slack-desc e il doinst.sh (contenuti nella directory install)
# $3 = Nome del pacchetto
_make_and_install_packages()
{
    echo -e "\nCopio la directory:\n\t- ${2}/install\nin:\n\t- ${1}" >> $LOG_FILE
    cd ${1};
    cp -pR ${2}/install . || return 1
    echo -ne "\nCreo il pacchetto ${3}${KERNEL_EXT}.tgz:" >> $LOG_FILE
    makepkg -l y -c n -p ${TMP_DIR}/${3}${KERNEL_EXT}.tgz >> $LOG_FILE 2>&1
    installpkg ${TMP_DIR}/${3}${KERNEL_EXT}.tgz | tee -a $LOG_FILE
    mv ${TMP_DIR}/${3}${KERNEL_EXT}.tgz "$DEST_DIR"

    cd $CWD
}


# Rimuove un pacchetto tramite removepkg. Prima controlla che il pacchetto esista e che sia
# univoco.
# Usata da:
# - _make_kernel_package
# - _make_modules_package (con l'uso del parametro $2)
# - _make_headers_package
# Parametri:
# $1 = Pacchetto da rimuovere
# $2 = Opzionale. Se fornito, il pacchetto verr rimosso solo se ha lo stesso nome di
#      ${1}${KERNEL_EXT} (che dovrebbe essere il pacchetto che si vuole installare)
# Valori di ritorno:
# 0 = Il pacchetto $1  stato rimosso
# 1 = Il pacchetto $1 non  stato rimosso perch, o non univoco oppure perch non esistente
# 2 = E' stato passato il parametro $2 e il pacchetto non ha il nome uguale a ${1}${KERNEL_EXT}
_remove_package()
{
    echo "Rimozione del pacchetto $1 ..." | tee -a $LOG_FILE
    local NUM_PACK=`ls -1 ${REP_LOG}/${1}* 2>/dev/null | wc -l`

    if [ $NUM_PACK -gt 1 ]; then
	echo -e "Ci sono pi di un pacchetto con questo nome. Rimozione non effettuata.\n" | tee -a $LOG_FILE
	return 1
    elif [ $NUM_PACK -eq 0 ]; then
	echo -e "Pacchetto non trovato.\n" | tee -a $LOG_FILE
	return 1
    else
	local PACK_TO_REMOVE=`basename \`ls -1 ${REP_LOG}/${1}*\``
	if [ $2 ]; then
	    if [ $PACK_TO_REMOVE != ${1}${KERNEL_EXT} ]; then
		return 2
	    fi
	fi
	echo -e "Rimuovo il pacchetto $PACK_TO_REMOVE" | tee -a $LOG_FILE
	echo
	removepkg $1 2>&1 >> $LOG_FILE
	return 0
    fi
}


### MAKE KERNEL PACKAGE ###
# Copia un file rinominandolo in $KERNEL_PACK/boot e ne crea un link simbolico
# Usata da:
# - _make_kernel_package
# Parametri:
# $1 = Path del file da copiare
# $2 = Nome del link da creare
_copy_and_link()
{
    cp $1 ${KERNEL_PACK}/boot/${2}${KERNEL_EXT}
    ( cd $KERNEL_PACK/boot; ln -s ${2}${KERNEL_EXT} $2 )
}

# Crea, installa e copia il pacchetto per l'immagine del kernel. Se esiste gi un pacchetto
# chiamato "kernel-image" lo rimuove.
_make_kernel_package()
{
    echo -e "Creo installo e copio il pacchetto dell'immagine del kernel ... \n" | tee -a $LOG_FILE

    mkdir ${KERNEL_PACK}/boot
    _copy_and_link $KERNEL_IMAGE vmlinuz
    _copy_and_link .config config
    _copy_and_link System.map System.map
    _remove_package kernel-image
    _make_and_install_packages $KERNEL_PACK $KERNEL_DIR kernel-image

    echo -e "immagine completata.\n" | tee -a $LOG_FILE
}
### END MAKE KERNEL PACKAGE ###


### MAKE MODULES PACKAGE ###
# Nella creazione del pacchetto dei moduli bisogna solo stare attenti a rimuovere
# il vecchio pacchetto solo dopo il riavvio del nuovo kernel, infatti i moduli
# sono ancora necessari al kernel attualmente caricato in memoria.

RC_DIR=/etc/rc.d       # Directory dei file rc

# Crea un file inserendoci le direttive per:
# - Rimuovere un pacchetto slackware
# - Rimuovere da un file rc ogni occorrenza del file appena creato
# - Rimuovere il file creato
# Usata da:
# - _remove_mudules_at_reboot
# Parametri:
# $1 = Pacchetto da rimuovere
# $2 = File rc da cui rimuovere le direttive
# $3 = File da creare
_rc_remove_modules()
{
    cat > ${RC_DIR}/${3} <<EOF
#!/bin/sh

echo "Rimozione del pacchetto $1"
removepkg $1 >& /dev/null&
sed -i /${3}/d ${RC_DIR}/$2&
rm ${RC_DIR}/${3}&
EOF
}


# Controlla se esiste un pacchetto chiamato "kernel-modules*$HOSTNAME". In caso
# affermativo prepara, usando il file rc $RC_FILE, la macchina affinch al prossimo
# riavvio venga rimosso il pacchetto.
_remove_mudules_at_reboot()
{
    _remove_package kernel-modules 1
    local RET=$?
    if [ $RET -eq 2 ]; then
	echo -e "\nTrovato un pacchetto dei moduli \"kernel-modules*\"" >> $LOG_FILE
	local PACK_TO_REMOVE=`ls ${REP_LOG}/kernel-modules*`
	local RC_FILE=rc.M
	local RC_REMOVE_MODULES=rc.remove_modules

	# Se non  gi stato fatto, indico in $RC_FILE che al prossimo riavvio si deve
	# eseguire $RC_REMOVE_MODULES
	if ! grep -q ${RC_DIR}/${RC_REMOVE_MODULES} ${RC_DIR}/${RC_FILE}; then
	    echo -e "Modifico il file:\n\t${RC_DIR}/${RC_FILE}\naffinch lanci:\n\t${RC_DIR}/${RC_REMOVE_MODULES}\n"\
"al prossimo riavvio." >> $LOG_FILE
cat >> ${RC_DIR}/${RC_FILE} <<EOF
[ -x ${RC_DIR}/${RC_REMOVE_MODULES} ] && ${RC_DIR}/${RC_REMOVE_MODULES}
EOF
	fi

	echo -e "Creo il file ${RC_DIR}/${RC_REMOVE_MODULES}\n" >> $LOG_FILE
	_rc_remove_modules $PACK_TO_REMOVE $RC_FILE $RC_REMOVE_MODULES
	chmod +x ${RC_DIR}/${RC_REMOVE_MODULES}
	echo -e "####################################################################"\
"\nATTENZIONE: Al prossimo riavvio il seguente pacchetto verr rimosso:\n\n"\
"$PACK_TO_REMOVE\n\n"\
"Se ci sono errori, controllare i file:\n - ${RC_DIR}/${RC_REMOVE_MODULES}\n - ${RC_DIR}/${RC_FILE}\n"\
"####################################################################\n" | tee -a $LOG_FILE
    fi
}


# Crea, installa e copia il pacchetto per i moduli del kernel. Se esiste gi un pacchetto
# chiamato "kernel-modules", imposta la macchina affinch al prossimo riavvio venga rimosso.
_make_modules_package()
{
    echo -e "Creo, installo e copio il pacchetto dei moduli del kernel ...\n" | tee -a $LOG_FILE

    # Sanity check. Controllo che tra le opzioni del "make" ci sia la possibilit
    # di installare i moduli nella directory INSTALL_MOD_PATH.
    if ! make help 2>/dev/null | grep -qE "modules_install.*INSTALL_MOD_PATH"; then
	echo '\nLa marca "modules_install.*INSTALL_MOD_PATH" manca, controllare make help\n' | tee -a $LOG_FILE
	return 1;
    fi

    echo "Installo i moduli nella directory ${MODULES_PACK}:" >> $LOG_FILE
    ! make modules_install INSTALL_MOD_PATH=$MODULES_PACK >> $LOG_FILE && return 1
    echo >> $LOG_FILE
    _remove_mudules_at_reboot &&
    _make_and_install_packages $MODULES_PACK $MODULES_DIR kernel-modules

    echo -e "moduli completati.\n" | tee -a $LOG_FILE
}
### END MAKE MODULES PACKAGE ###


### MAKE HEADERS PACKAGE ###
# Crea, installa e copia il pacchetto per gli headers del kernel. Se esiste gi un pacchetto
# chiamato "kernel-headers, lo rimuove.
_make_headers_package()
{
    echo -e "Creo, installo e copio il pacchetto degli headers del kernel...\n" | tee -a $LOG_FILE

    # Sanity check. Controllo che tra le opzioni del "make" ci sia la possibilit
    # di installare gli headers nella directory $INSTALL_HDR_PATH.
    if ! make help 2>/dev/null | grep -qE "headers_install.*INSTALL_HDR_PATH"; then
	echo -e '\nLa marca "headers_install.*INSTALL_HDR_PATH" manca, controllare make help\n' | tee -a $LOG_FILE
	return 1;
    fi

    local TEMP=$KERNEL_EXT
    KERNEL_EXT=-${KERNEL_VERSION}-noarch-1
    mkdir ${HEADERS_PACK}/usr
    echo "Installo gli headers nella directory ${HEADERS_PACK}/usr:" >> $LOG_FILE
    ! make headers_install INSTALL_HDR_PATH=${HEADERS_PACK}/usr >> $LOG_FILE  && return 1
    echo >> $LOG_FILE
    _remove_package kernel-headers
    _make_and_install_packages $HEADERS_PACK $HEADERS_DIR kernel-headers
    KERNEL_EXT=$TEMP

    echo -e "headers completati.\n" | tee -a $LOG_FILE
}
### END MAKE HEADERS PACKAGE ###


# Stampa tanti asterischi '*' per quanto  il valore $1
# Usata durante l'esecuzione degli STEP (si veda sotto)
_stampa_asterischi()
{
    local NUM=$1

    while [ $NUM -gt 0 ]; do
	echo -n "*" | tee -a $LOG_FILE
	let NUM--
    done
}


# Lancia lilo (se lo trova)
# Usata durante l'esecuzione degli STEP (si veda sotto)
_run_lilo()
{
    echo "Lancio lilo, se lo trovo:" | tee -a $LOG_FILE

    if [ ! -f /etc/lilo.conf ]; then
	echo "/etc/lilo.conf non trovato" | tee -a $LOG_FILE
	return 0
    fi

    echo "Trovato /etc/lilo.conf" | tee -a $LOG_FILE
    if [ -x /sbin/lilo ]; then
	echo -e "Trovato /sbin/lilo, lo eseguo:\n" | tee -a $LOG_FILE
	local LILO_OUTPUT=`mktemp -p ${TMP_DIR} lilo_output_installkernel.XXXXXX`
	echo -e "Creo un file temporaneo in cui inserire l'output di lilo"\
	    "\nIl file creato si chiama:\n\n\t$LILO_OUTPUT\n" >> $LOG_FILE
	local LILO_ERROR=0;
	if ! /sbin/lilo >> $LILO_OUTPUT 2>&1; then
	    LILO_ERROR=1;
	    echo -e "ERRORE: lilo  ritornato con un valore di errore\n" | tee -a $LOG_FILE
	fi

	echo -e "Output di lilo:\n" | tee -a $LOG_FILE
	cat $LILO_OUTPUT | tee -a $LOG_FILE

	echo | tee -a $LOG_FILE

	[ $LILO_ERROR -eq 1 ] && return 1
    else
	echo "/sbin/lilo non trovato o non  eseguibile" | tee -a $LOG_FILE
    fi

    return 0
}


## LO SCRIPT INIZIA QUI: ##
echo -e "\n/sbin/installkernel v.1.1\nby Spina <spina80@gmail.com>\n" | tee $LOG_FILE
echo -e "\n#### Un LOG delle operazioni svolte verr creato nel file $LOG_FILE ####\n\n"

# Stampa del men di scelta
while [ -z $SCELTA ]; do
    echo -en "Premi:\n\t1) Per creare SOLO il pacchetto degli HEADERS\n"\
	"\t2) Per creare SOLO il pacchetto dei MODULI\n"\
	"\t3) Per creare SOLO il pacchetto dell'IMMAGINE\n"\
	"\t4) Per creare SOLO i pacchetti degli HEADERS e dei MODULI\n"\
	"\t5) Per creare SOLO i pacchetti degli HEADERS e dell'IMMAGINE\n"\
	"\t6) Per creare SOLO i pacchetti dei MODULI e dell'IMMAGINE\n"\
	"\t7) TUTTI e tre I PACCHETTI\n\n"\
	"(7) => "
    read -n1 SCELTA

    [ -z $SCELTA ] && SCELTA=7 && break  # Si  premuto solo invio

    echo -e "\n"

    if ! echo $SCELTA | grep -q [[:digit:]]; then # Non si  inserita una cifra
	unset SCELTA;
	continue;
    fi

    [ $SCELTA -gt 7 -o $SCELTA -le 0 ] && unset SCELTA; # Si  inserita una cifra non facente parte del range
done

H_FLAG=0;
M_FLAG=0;
I_FLAG=0;
case $SCELTA in
    1) H_FLAG=1;;
    2) M_FLAG=1;;
    3) I_FLAG=1;;
    4) H_FLAG=1; M_FLAG=1;;
    5) H_FLAG=1; I_FLAG=1;;
    6) M_FLAG=1; I_FLAG=1;;
    7) H_FLAG=1; M_FLAG=1; I_FLAG=1;;
esac

echo
echo -e "H_FLAG=${H_FLAG}\nM_FLAG=${M_FLAG}\nI_FLAG=${I_FLAG}\n" >> $LOG_FILE

# Preparo le directory temporanee
echo -e "Rimuovo, se esistono, le directory:\n\t- $KERNEL_PACK\n\t- $MODULES_PACK"\
"\n\t- $HEADERS_PACK" >> $LOG_FILE
rm -rf $KERNEL_PACK $MODULES_PACK $HEADERS_PACK || exit 1;
echo -e "\nCreo le directory:\n\t- $KERNEL_PACK\n\t- $MODULES_PACK\n\t- $HEADERS_PACK\n" >> $LOG_FILE
mkdir  $KERNEL_PACK $MODULES_PACK $HEADERS_PACK || exit 1

# Esecuzione della creazione dei pacchetti
STEP=0;
while [ $H_FLAG -eq 1 -o $M_FLAG -eq 1 -o $I_FLAG -eq 1 ]; do
    let STEP++
    _stampa_asterischi $STEP

    echo -e " STEP $STEP:" | tee -a $LOG_FILE

    if [ $H_FLAG = 1 ]; then
	if ! _make_headers_package; then
	    exit 1;
	fi
	H_FLAG=0;
	continue
    fi

    if [ $M_FLAG = 1 ]; then
	if ! _make_modules_package; then
	    exit 1;
	fi
       M_FLAG=0;
       continue
    fi

    if [ $I_FLAG = 1 ]; then
	if ! _make_kernel_package; then
	    exit 1;
	fi

	if ! _run_lilo; then
	    exit 1
	fi

	I_FLAG=0;
	continue
    fi
done

echo -e "FINE" | tee -a $LOG_FILE