Criando arquivos .run no Linux – o jeito difícil (mas nem tanto!)

Se você já baixou os drivers para sua placa de vídeo (nVidia, por exemplo) e teve que instalá-lo manualmente, já deve ter topado com um arquivo com extensão “.run”. Esse arquivo é um executável self extract que, no Linux, nada mais é do que um script com um arquivo binário “atachado”.

A idéia é ter um script que sabe como localizar essa parte binária do arquivo, descompactá-lo num diretório desejado e executar algum script contido no pacote. Algo mais ou menos assim:

#!/bin/bash

# run.sh
# Este é o script que é a parte "executável" do arquivo .run

# Tamanho do run.sh... deve ser EXATAMENTE o tamanho 
# deste script. Veja adiante como alterá-lo!
SCRIPT_SIZE=@SCRIPTSZ@

# Diretório padrão onde nosso archive descompactará
# os arquivos.
MYDIR=~/mydir

SCRIPT="$0"
TOTAL_SIZE=`cat "$SCRIPT" | wc -c`
TAR_SIZE=$((TOTAL_SIZE - SCRIPT_SIZE))

check_root_privileges()
{
  if [ $UID -ne 0 ]; then
    echo "root privileges needed.\nUse sudo."
    exit 1
  fi
}

check_target_dir()
{
  if [ ! -d "$MYDIR" ]; then
    echo "Target directory '$MYDIR$' don't exist." \
         " Creating..."

    # TODO: maybe you should modidify the ownership 
    #       after creating this directory!
    mkdir "$MYDIR"
  fi
}

test_archive()
{
  echo "Testing archive integrity..."
  dd if="$SCRIPT" ibs=1 skip=$SCRIPT_SIZE \
     count=$TAR_SIZE 2>&1 /dev/null | \
    tar tz -

  if [ $# -ne 0 ]; then
    echo "Archive integrity fail! Aborting..."
    exit 2
  else
    echo "Integrity test ok."
  fi
}

extract()
{
  echo "Extracting files..."
  dd if="$SCRIPT" ibs=1 skip=$SCRIPT_SIZE \
     count=$TAR_SIZE 2>&1 /dev/null | \
    tar xz - -C "$MYDIR"

  if [ $# -eq 0 ]; then
    echo "Done. Files extracted to '$MYDIR'."
  else
    echo "Error extracting files. Aborting."
    rm -rf "$MYDIR"
    exit 3
  fi
}

run_installer()
{
  cd "$MYDIR" && ./install-script.sh
}

check_root_privileges
check_target_dir
test_archive
extract
run_installer

Onde @SCRIPTSZ@ é o tamanho do script run.sh, em bytes, obtido com:

$ sed -i "s/@SCRIPTSZ@/`cat run.sh | wc -c`/" run.sh

Você pode ter que modificar @SCRIPTSZ@ manualmente ou descobrir algum jeito interessante de calcular o tamanho do arquivo depois de escrever esse tamanho… O importante é que SCRIPT_SIZE tenha exatamente o tamanho de run.sh depois que ele estiver pronto!

Um macete que você pode achar interessante é este:

#!/bin/bash
cp run.sh run$$.sh

for i in {1..3}; do
  SZ=`cat run$$.sh | wc -c`
  sed "s/@SCRIPTSZ@/$SZ/" run.sh > run$$.sh
done

mv run$$.sh run.sh

A primeira iteração do loop pega o tamanho de run.sh e coloca em @SCRIPTSZ@. A segunda iteração do loop pega o tamanho de run$$.sh e o atualiza ($$ é substituído pelo PID do script). Por que dois passos? Suponha que o arquivo run.sh original tenha 100 bytes de tamanho. No primeiro passo a string “@SCRIPTSZ@” é substituída por “100”. O arquivo passa a ter 7 bytes a menos, ou seja 93 bytes. Na segunda passagem o valor “93” é colocado em “@SCRIPTSZ@”, fazendo com que o script tenha 8 bytes a menos que o original! No terceiro passo o valor 92 é colocado em @SCRIPTSZ@ e é, finalmente, o valor correto!

É claro, esse processo pode ser feito manualmente de forma bem simples:

$ ls -og run.sh
-rw-r--r-- 1 385 Apr 24 09:20 run.sh

... edite run.sh e coloque 378 (385 - 7) 
    no lugar de @SCRIPTSZ@ ...

O motivo de subtrairmos 3 do tamanho do arquivo é que “@SCRIPTSZ@” tem 10 caracteres e “385” tem 3. Ao modificar a string perderemos 7 caracteres.

Para criar o arquivo “myarchive.run” você só precisará fazer:

$ tar czf myarchive.tar.gz -C ~/mydir/ *
$ cat run.sh myarchive.tar.gz > myselfextr.run

Onde “myarchive.tar” é o arquivo compactado com tudo o que você quer distribuir, incluindo o script “install-script.sh”, que será chamado por “run.sh”.

Anúncios

3 comentários sobre “Criando arquivos .run no Linux – o jeito difícil (mas nem tanto!)

  1. Interessante… fiz alguns testes tentando evitar o cálculo para colocar no lugar de @SCRIPTSZ@, fiz algumas alterações no seu script e agora é possível apenas copiar o arquivo “.tgz” ao final deste script modificado que ele identifica aonde está o início do arquivo compactado.

    #!/bin/bash
    
    # run.sh
    # Este é o script que é a parte "executável" do arquivo .run
    
    # Diretório padrão onde nosso archive descompactará
    # os arquivos.
    MYDIR=~/mydir
    
    SCRIPT="$0"
    
    check_root_privileges()
    {
      if [ $UID -ne 0 ]; then
        echo "root privileges needed.\nUse sudo."
        exit 1
      fi
    }
    
    check_target_dir()
    {
      if [ ! -d "$MYDIR" ]; then
        echo "Target directory '$MYDIR$' don't exist." \
             " Creating..."
    
        # TODO: maybe you should modidify the ownership
        #       after creating this directory!
        mkdir "$MYDIR"
      fi
    }
    
    test_archive()
    {
      echo "Testing archive integrity..."
      tail -$(( $(cat ${SCRIPT} | wc -l)-$(grep  --binary-files=text -n '^#####EOF#####$' ${SCRIPT} | cut -f1 -d:)+1 )) ${SCRIPT} | tar -zt
    
      if [ $# -ne 0 ]; then
        echo "Archive integrity fail! Aborting..."
        exit 2
      else
        echo "Integrity test ok."
      fi
    }
    
    extract()
    {
      echo "Extracting files..."
      tail -$(( $(cat ${SCRIPT} | wc -l)-$(grep  --binary-files=text -n '^#####EOF#####$' ${SCRIPT} | cut -f1 -d:)+1 )) ${SCRIPT} | tar -C "${MYDIR}" -zx
    
      if [ $# -eq 0 ]; then
        echo "Done. Files extracted to '$MYDIR'."
      else
        echo "Error extracting files. Aborting."
        rm -rf "$MYDIR"
        exit 3
      fi
    }
    
    run_installer()
    {
      cd "$MYDIR" && ./install-script.sh
    }
    
    check_root_privileges
    check_target_dir
    test_archive
    extract
    run_installer
    exit 0
    #####EOF#####
    1. Boa idéia colocar um marcador ao final do script… Thanks!
      Please, da próxima vez que postar um código, formate-o (com <pre> e </pre>> ou use um pastebin.

      PS: Acredito que se você fizer [code language=”bash”] … código … [/code]. tb funcione.

      Veja ai como ficou, acima!…

      Anyway! GRANDE DICA!

Deixe um comentário

Faça o login usando um destes métodos para comentar:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s