Como fazer um gerenciador de pacotes esperar se outra instância do APT estiver em execução?

50

Eu já vi muitos softwares, como o Update Manager e o Synaptic Package Manager, eles esperam se algum outro programa estiver usando o /var/lib/dpkg/locke está bloqueado. Como podemos fazer isso através do terminal? Eu vi apt-geto manual, mas não achei nada útil.

Piyush
fonte
bem, você pode executar vários comandos apt-get. Gosto sudo apt-get install packagename && sudo apt-get updatee eles acontecerão automáticos um após o outro.
Alvar
11
Isso não faz isso sudo apt-get install packagename1 packagename2 packagename3 :?
Rinzwind
(Veja unix.stackexchange.com/questions/463498/... se você está no caso especial em que você está tentando esperar por tempo de inicialização aptos a terminar no Ubuntu 18.04 e acima)
Anon

Respostas:

35

Você pode usar o aptdconcomandoÍcone da página de manual para enfileirar tarefas do gerenciador de pacotes se comunicando com o aptdaemon em vez de usar o apt-get diretamente.

Então, basicamente, você pode simplesmente fazer o que sudo aptdcon --install chromium-browserquer que seja e enquanto esse comando estiver em execução, você poderá executá-lo novamente, mas instalar pacotes diferentes e o apt-daemon irá enfileirá-los em vez de errar.

Isso é especialmente útil se você estiver fazendo uma atualização longa ou algo assim e quiser continuar instalando pacotes ou se estiver criando scripts juntos e quiser garantir que a instalação seja mais confiável.

Jorge Castro
fonte
4
Pode aptdconser executado de forma a aceitar qualquer solicitação, como apt-get -yfaz?
Noki
4
yes | aptdcon --hide-terminal --install "package"Ocultar terminal é necessário; caso contrário, a tubulação yescausará problemas.
whitehat101
11
O problema com esta resposta é que ela requer instalação aptdcon. Estou trabalhando em um script de auto-inicialização que faz a configuração inicial de um VPS, onde um processo em segundo plano também faz algumas configurações iniciais. Não consigo instalar aptdconaté conseguir solucionar o bloqueio.
Fake Name
2
@FakeName para configuração inicial do servidor que você provavelmente vai querer usar cloud-init: cloudinit.readthedocs.io/en/latest
Jorge Castro
11
em que pacote está aptdcon?
ctrl-alt-Delor
45

Você pode apt-getaprender a esperar se outro gerenciador de software estiver em execução. Algo semelhante ao comportamento do próximo elenco da tela:

insira a descrição da imagem aqui

Como eu fiz isso?

Eu crio um novo script chamado apt-get(wrapper for apt-get) no /usr/local/sbindiretório com o seguinte código bash:

#!/bin/bash

i=0
tput sc
while fuser /var/lib/dpkg/lock >/dev/null 2>&1 ; do
    case $(($i % 4)) in
        0 ) j="-" ;;
        1 ) j="\\" ;;
        2 ) j="|" ;;
        3 ) j="/" ;;
    esac
    tput rc
    echo -en "\r[$j] Waiting for other software managers to finish..." 
    sleep 0.5
    ((i=i+1))
done 

/usr/bin/apt-get "$@"

Não se esqueça de torná-lo executável:

sudo chmod +x /usr/local/sbin/apt-get

Antes de testar, verifique se está tudo bem. A saída do which apt-getcomando deve ser agora /usr/local/sbin/apt-get. O motivo é: por padrão, o /usr/local/sbindiretório é colocado antes do /usr/bindiretório no usuário ou raiz PATH.

Radu Rădeanu
fonte
isso é realmente um golpe direto na minha pergunta. Obrigado :)
rɑːdʒɑ
2
Como seu script gerencia instâncias empilhadas do apt-get? Tipo, antes que termine eu lancei mais 5 apt-get?
Braiam
@Braiam Atenciosamente, eu não sei; Eu não sou um bom testador. Você testou? Se surgirem problemas, o script poderá ser aprimorado criando um novo bloqueio com carimbo de data e hora quando uma nova instância do script for iniciada (apenas um exemplo - também existem muitas outras maneiras).
Radu Rădeanu
Uau! eloquente. funciona como um encanto com tudo que joguei até agora! Dois polegares ... este deve ser apenas parte do pacote: D
Eric
2
Perfeito! Eu tive um problema ao usar o AWS cloudinit para instalar pacotes em um novo servidor, mas o Ubuntu faz algumas aptcoisas na inicialização, então meu dpkgcomando falhou devido ao dpkg db bloqueado. Eu coloquei essa linha em meus dados de usuário na nuvem: while fuser /var/lib/dpkg/lock >/dev/null 2>&1; do sleep 1; done; dpkg -i package.debe funciona muito bem.
Volodymyr Smotesko
20

Além do óbvio &&, você pode estar procurando aptdcon. Esta ferramenta é capaz de detectar outras instâncias do apt e esperar que elas terminem:

sudo aptdcon --safe-upgrade
[/] 11% Esperando o encerramento de outros gerentes de software Aguardando aptidão

(Estou executando o aptitude em outro lugar)

A vantagem dessa ferramenta é que você pode estocar várias ações consecutivamente sem se preocupar com o que fará em seguida. aptdconé ideal para scripts autônomos e instalação da GUI, pois você pode permitir que a ferramenta seja executada em segundo plano para não bloquear seu front-end.

As operações suportadas por aptdconsão:

  • --refresh, -c: Isso é equivalente a apt-get update. Ele atualiza sua lista de pacotes.
  • --install, --remove, --upgrade, --purge, --downgrade. Cada um deles faz como seus nomes dizem. O nome do (s) pacote (s) é obrigatório. -i, -r, -u, -p: Estes são os curtas opções para todos, exceto downgrade, que não tem um.
  • --safe-upgrade, --full-upgradesão os equivalentes a apt-get's upgrade/ dist-upgradee aptitude' s safe-upgrade/ full-upgrade. Estes não precisam de parâmetros.
  • Existem várias outras operações, que podem ser encontradas no manual. Porém, esses são os mais utilizados pelos usuários interessados aptd. Há opções que se sobrepõem com o que apt-key, apt-cache, dpkgfazer.

apt-getele próprio não suporta esses métodos (para aguardar outras instâncias do apt), assim aptdconcomo a solução preferida para os gerenciadores de pacotes da GUI: a USC usa aptdcomo back-end, o mesmo que o Synaptic. Outra solução é packagekit, mas ainda não suporta a função que você está procurando.

Braiam
fonte
11
aptdcon --install /path/to/pgk.debFunciona como dpkg -i, embora eu não tenha conseguido encontrar isso explicitamente mencionado no manual.
whitehat101
Posso usar isso no script pós-instalação em um pacote?
Nelson Teixeira
@NelsonTeixeira, por que você usaria isso no script ppst-install? Se a pós-instalação estiver sendo executada, obviamente o apt está em execução.
Braiam 3/17/17
Quero fazer com que o postinstall instale alguns pacotes especiais que não estão no repositório. Isso é possível ? Eu os incluiria no pacote e o script os instalaria após a conclusão da instalação.
Nelson Teixeira
@NelsonTeixeira novamente, por que seria necessário? Os scripts de pós-instalação são executados apenas quando há uma instalação de pacote. Sugiro que faça uma pergunta e explique seu caso com mais detalhes.
Braiam 3/17/17
18

Uma abordagem muito simples seria um script que esperasse o bloqueio não ser aberto. Vamos chamá-lo waitforapte colocá -lo em /usr/local/bin:

#!/bin/sh

while sudo fuser /var/{lib/{dpkg,apt/lists},cache/apt/archives}/lock >/dev/null 2>&1; do
   sleep 1
done

Então apenas corra sudo waitforapt && sudo apt-get install whatever. Você pode adicionar exceções sudoerspara permitir que você a execute sem precisar de uma senha (você precisará dela para apt-getque não haja grande ganho).

Infelizmente, isso não enfileira as coisas. Dado que algumas das operações do apt são interativas ("Tem certeza de que deseja remover todos esses pacotes ?!"), não consigo ver uma boa maneira de contornar isso ...

Oli
fonte
talvez -yassumir sim para todas as operações?
Braiam
Obrigado. Acho que posso usá-lo melhor se usar ajuda de alias.
rɑːdʒɑ
3
No não certo -yé desejável embora.
Oli
11
Eu não acho que você quer envolver sudo fuserem [[ $(...) ]]. Retorna um código de saída zero quando o arquivo está sendo acessado, o que deve ser suficiente.
kiri
4
Eu achei a solução a seguir mais completa, pois existem vários bloqueios envolvidos ao trabalhar com o apt-get: while sudo fuser /var/lib/dpkg/lock /var/lib/apt/lists/lock /var/cache/apt/archives/lock >/dev/null 2>&1; do echo 'Waiting for release of dpkg/apt locks'; sleep 5; done; sudo apt-get -yy update
Manuel Kießling
3

Você pode usar uma técnica de pesquisa:

$ time (while ps -opid= -C apt-get > /dev/null; do sleep 1; done); \
  apt-get -y install some-other-package
malte
fonte
melhor usarinotify
ctrl-alt-delor 08/09/17
3

Eu fiz um script que faz isso:

#!/bin/bash

# File path to watch
LOCK_FILE='/var/lib/dpkg/lock'

# tput escape codes
cr="$(tput cr)"
clr_end="$(tput el)"
up_line="$(tput cuu 1)"

CLEAN(){
    # Cleans the last two lines of terminal output,
    # returns the cursor to the start of the first line
    # and exits with the specified value if not False

    echo -n "$cr$clr_end"
    echo
    echo -n "$cr$clr_end$up_line"
    if [[ ! "$1" == "False" ]]; then
        exit $1
    fi
}

_get_cmdline(){
    # Takes the LOCKED variable, expected to be output from `lsof`,
    # then gets the PID and command line from `/proc/$pid/cmdline`.
    #
    # It sets `$open_program` to a user friendly string of the above.

    pid="${LOCKED#p}"
    pid=`echo $pid | sed 's/[\n\r ].*//'`
    cmdline=()
    while IFS= read -d '' -r arg; do
        cmdline+=("$arg")
    done < "/proc/${pid}/cmdline"
    open_program="$pid : ${cmdline[@]}"
}

# Default starting value
i=0

# Checks if the file is locked, writing output to $FUSER
while LOCKED="$(lsof -F p "$LOCK_FILE" 2>/dev/null)" ; do
    # This will be true if it isn't the first run
    if [[ "$i" != 0 ]]; then
        case $(($i % 4)) in
            0 ) s='-'
                i=4
                _get_cmdline # Re-checks the command line each 4th iteration
            ;;
            1 ) s=\\ ;;
            2 ) s='|' ;;
            3 ) s='/' ;;
        esac
    else
        # Traps to clean up the printed text and cursor position
        trap "CLEAN False; trap - SIGINT ; kill -SIGINT $$" SIGINT
        trap 'CLEAN $((128+15))' SIGTERM
        trap 'CLEAN $((128+1))' SIGHUP
        trap 'CLEAN $((128+3))' SIGQUIT

        # Default starting character
        s='-'

        _get_cmdline
        echo -n "$save_cur"
    fi
    # Prints the 2nd line first so the cursor is at the end of the 1st line (looks nicer)
    echo
    echo -n "$cr$clr_end$open_program"
    echo -n "$up_line$res_cur$cr$clr_end[$s] Waiting for other package managers to finish..."
    #echo -en "$cr$clr_end[$s] Waiting for other package managers to finish..."
    #echo -en "\n$cr$clr_end$open_program$cr$up_line"
    ((i++))
    sleep 0.025
done

CLEAN False

# This allows saving the script under a different name (e.g. `apt-wait`)
# and running it. It only imitates `apt-get` if it was launched as such
if [[ "${0##*/}" == 'apt-get' ]]; then
    exec /usr/bin/apt-get "$@"
    exit $?
fi

Salve o acima em /usr/local/sbin/apt-get. apt-getaguardará se outra instância já estiver em execução.

Como alternativa, salve-a como /usr/local/sbin/apt-waitexemplo de uso:

apt-wait && aptitude

que será executado aptitudeapós a saída do processo atual que contém o bloqueio.

Exemplo de execução:

  1. Primeiro, um apt-getcomando é executado, por exemplo:

    $ sudo apt-get remove some_package
    
  2. Em outro terminal, outro comando é executado:

    $ sudo apt-get install some_other_package
    

    Ele aguardará o término do primeiro comando e a execução. Saída em espera:

    [/] Waiting for other package managers to finish...
          28223 : /usr/bin/apt-get remove some_package
    
kiri
fonte
11
melhor usarinotify
ctrl-alt-delor 08/09/17
3

Infelizmente, o fusor não faz muito por você quando você está executando em contêineres de namespace sem privilégios diferentes, como o lxc.

Além disso, o aptdcon não é instalado por padrão (pelo menos no 18.04) e coloca sua tarefa em segundo plano em uma fila para que você perca a serialização. Isso não é intransponível, mas significa que sua automação precisa ter uma maneira de evitar erros de agrupamento no apt durante a instalação do aptdcon, e você precisará de algum tipo de espera para qualquer coisa que precise serializar após a instalação de pacotes via aptdcon a menos que já exista algum tipo de sinalizador para isso.

O que funciona é rebanho. Isso também deve funcionar com o NFS etc., pois usa o bloqueio do sistema de arquivos da mesma maneira que o apt, apenas com o parâmetro -w segundos que ele aguardará no seu bloqueio em vez de gerar um erro.

Portanto, seguindo o modelo do wrapper, adicione-o como apt-get em / usr / local / bin / e compartilhe.

Isso também tem o benefício de limitar a E / S, não permitindo o paralelismo no apt, para que você possa permitir que o cron acione atualizações à meia-noite em qualquer lugar, sem bater o disco.

#!/bin/bash
exec /usr/bin/flock -w 900 -F --verbose /var/cache/apt/archives/lock /usr/bin/apt-get $@  

Um pedido de recurso muito agradável e simples para o apt-get seria um sinalizador -w para alternar para um bloqueio de bloqueio / espera.

Raymond Ferguson
fonte
O apt usa fcntl, não flock, então isso não funcionará. askubuntu.com/questions/1077215/…
Andy
Isso funciona perfeitamente e é uma opção melhor do que usar fusercomo mostrado em muitas respostas, porque fuserainda é propenso a condições de corrida entre assistir a trava e o processo de destino pegando a trava novamente. Com flocko bloqueio é adquirido e depois passado para o processo de destino, para que não haja tempo para perder o bloqueio nesse meio tempo.
jrudolph 17/10
1

Uma linha com base na resposta de Oli :

while sudo fuser /var/{lib/{dpkg,apt/lists},cache/apt/archives}/lock >/dev/null 2>&1; do sleep 1; done
Menasheh
fonte