Comment faire en sorte qu'un gestionnaire de paquets attende si une autre instance d'APT est en cours d'exécution?

50

J'ai vu de nombreux logiciels tels que Update Manager et Synaptic Package Manager, ils attendent si un autre programme utilise le /var/lib/dpkg/locket est verrouillé. Comment pouvons-nous faire cela par le terminal? J'ai vu apt-getle manuel mais je n'ai rien trouvé d'utile.

Piyush
la source
Eh bien, vous pouvez faire plusieurs commandes apt-get. Comme sudo apt-get install packagename && sudo apt-get updateet ils vont se passer automatiquement les uns après les autres.
Alvar
1
Est-ce que cela ne fait pas ça sudo apt-get install packagename1 packagename2 packagename3 ?
Rinzwind
(Voir unix.stackexchange.com/questions/463498/… si vous êtes dans le cas particulier où vous essayez d'attendre le temps de démarrage pour finir sur Ubuntu à partir de 18.04 et plus)
Anon

Réponses:

35

Vous pouvez utiliser cette aptdconcommandeIcône de page de manuel pour mettre en file d'attente les tâches du gestionnaire de paquets en communiquant avec aptdaemon au lieu d'utiliser apt-get directement.

Donc, en gros, vous pouvez simplement faire sudo aptdcon --install chromium-browserce que vous voulez et tant que cette commande est en cours d'exécution, vous pouvez l'exécuter à nouveau, mais installer différents packages et apt-daemon les mettra en file d'attente au lieu de générer une erreur.

Ceci est particulièrement utile si vous effectuez une longue mise à niveau ou quelque chose comme cela et que vous souhaitez continuer à installer des packages ou si vous écrivez quelque chose ensemble et que vous voulez vous assurer que l'installation sera plus fiable.

Jorge Castro
la source
4
Peut aptdconêtre exécuté de telle sorte qu'il accepte les invites comme il le apt-get -yfait?
Noki
4
yes | aptdcon --hide-terminal --install "package"Cacher le terminal est nécessaire, sinon la tuyauterie yescausera des problèmes.
whitehat101
1
Le problème avec cette réponse est qu'il nécessite une installation aptdcon. Je travaille sur un script d'amorçage qui effectue la configuration initiale d'un VPS, où un processus en arrière-plan effectue également une configuration initiale. Je ne peux pas installer aptdconavant de pouvoir contourner le verrou.
faux nom
2
@FakeName pour la configuration initiale du serveur, vous voudrez probablement utiliser cloud-init: cloudinit.readthedocs.io/en/latest
Jorge Castro
1
quel paquet est dedans aptdcon?
ctrl-alt-delor
45

Vous pouvez apt-getapprendre à attendre si un autre gestionnaire de logiciel est en cours d'exécution. Quelque chose de similaire avec le comportement de la prochaine distribution d'écran:

entrez la description de l'image ici

Comment je l'ai fait?

Je crée un nouveau script appelé apt-get(wrapper for apt-get) dans le /usr/local/sbinrépertoire avec le code bash suivant:

#!/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'oubliez pas de le rendre exécutable:

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

Avant de tester, vérifiez si tout va bien. La sortie de which apt-getcommande devrait être maintenant /usr/local/sbin/apt-get. La raison est la suivante: par défaut, le /usr/local/sbinrépertoire est placé avant le /usr/binrépertoire utilisateur ou root PATH.

Radu Rădeanu
la source
c'est vraiment un coup direct à ma question. merci :)
rɑːdʒɑ
2
Comment votre script gère-t-il les instances empilées d'apt-get? Avant de terminer, j’ai lancé 5 autres apt-get?
Braiam
@Braiam Sincèrement, je ne sais pas; Je ne suis pas un bon testeur. L'avez-vous testé? Si des problèmes apparaissent, vous pouvez améliorer le script en créant un nouveau verrou avec horodatage lors du démarrage d'une nouvelle instance du script (juste un exemple - il existe également de nombreuses autres façons).
Radu Rădeanu
Hou la la! éloquent. fonctionne comme un charme avec tout ce que j'ai jeté à ce jour! Bravo, cela devrait faire partie du paquet: D
Eric
2
C'est parfait! J'ai eu un problème d'utilisation de AWS cloudinit pour installer des packages sur un nouveau serveur, mais Ubuntu effectuant certaines tâches au aptdémarrage, ma dpkgcommande a donc échoué à cause du blocage de dpkg db. J'ai mis ce one-liner dans mes données utilisateur cloudinit: while fuser /var/lib/dpkg/lock >/dev/null 2>&1; do sleep 1; done; dpkg -i package.debet cela fonctionne très bien.
Volodymyr Smotesko le
20

En dehors de l'évidence &&, vous recherchez peut-être aptdcon. Cet outil est capable de détecter d'autres instances d'apt et d'attendre qu'elles se terminent:

sudo aptdcon --safe-upgrade
[/] 11% En attente d'abandon des autres gestionnaires de logiciels En attente d'aptitude

(Je cours d'aptitude ailleurs)

L'avantage de cet outil est que vous pouvez stocker plusieurs actions de manière consécutive sans vous inquiéter de ce que vous ferez ensuite. aptdconest idéal pour les scripts sans assistance et l’installation d’interface graphique, car vous pouvez autoriser l’exécution de l’outil en arrière-plan sans bloquer votre interface.

Les opérations supportées par aptdconsont:

  • --refresh, -c: Ceci est l'équivalent de apt-get update. Il met à jour votre liste de paquets.
  • --install, --remove, --upgrade, --purge, --downgrade. Chacun fait comme son nom l'indique. Le nom du ou des colis est obligatoire. -i, -r, -u, -p: Ce sont les options courtes pour tous , sauf révision à la baisse, qui ne l' ont pas.
  • --safe-upgrade, --full-upgradesont les contreparties de apt-get' upgrade/ dist-upgradeet aptitude' safe-upgrade/ full-upgrade. Ceux-ci n'ont pas besoin de paramètres.
  • Il existe plusieurs autres opérations, qui peuvent être trouvées dans le manuel. Mais, ce sont les plus utilisés par les utilisateurs intéressés par aptd. Il y a des options qui se chevauchent avec ce que apt-key, apt-cache, dpkgfaire.

apt-getlui-même ne supporte pas de telles méthodes (pour attendre d'autres instances d'apt), c'est donc aptdconla solution préférée par les gestionnaires de paquets de l'interface graphique: USC utilise aptdcomme back-end, comme Synaptic. Une autre solution est packagekit, mais elle ne supporte pas la fonction que vous recherchez (pour le moment).

Braiam
la source
1
aptdcon --install /path/to/pgk.debFonctionne comme dpkg -i, bien que je ne pouvais trouver ce explicitement mentionné dans le manuel.
whitehat101
Puis-je l'utiliser dans un script post-installation dans un package?
Nelson Teixeira
@ NelsonTeixeira pourquoi voudriez-vous utiliser cela dans le script ppst-install? Si la post-installation est en cours d'exécution, évidemment, apt est en cours d'exécution.
Braiam
Je souhaite que postinstall installe des paquets spéciaux qui ne sont pas dans le référentiel. Est-ce possible ? Je les inclurais dans un paquet, puis le script les installerait une fois l'installation terminée.
Nelson Teixeira
@NelsonTeixeira encore, pourquoi ce serait nécessaire? Les scripts de post-installation ne s'exécutent que lorsqu'il y a une installation de paquet. Je vous suggère de poser une question à la place et d'expliquer votre cas de manière plus détaillée.
Braiam
18

Une approche très simple serait un script qui attend que le verrou ne soit pas ouvert. Appelons-le waitforaptet collez-le dans /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

Alors viens courir sudo waitforapt && sudo apt-get install whatever. Vous pouvez ajouter des exceptions dans sudoerspour vous permettre de l'exécuter sans avoir besoin d'un mot de passe (vous en aurez besoin pour le, apt-getdonc ce n'est pas un gros gain).

Malheureusement, cela ne fait pas la queue. Étant donné que certaines opérations d'apt sont interactives ("Êtes-vous sûr de vouloir supprimer tous ces paquets?!"), Je ne vois pas comment contourner ce problème ...

Oli
la source
peut-être -ysupposer que oui pour toutes les opérations?
Braiam
Je vous remercie. Je pense que je peux mieux l’utiliser si j’utilise alias help.
Départ
3
En pas sûr -yest souhaitable si.
Oli
1
Je ne pense pas que vous voulez envelopper sudo fuserdans [[ $(...) ]]. Il renvoie un code de sortie zéro lors de l'accès au fichier, ce qui devrait suffire.
Kiri
4
J'ai trouvé la solution suivante plus complète, car plusieurs verrous sont impliqués dans le travail avec 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

Vous pouvez utiliser une technique d'interrogation:

$ time (while ps -opid= -C apt-get > /dev/null; do sleep 1; done); \
  apt-get -y install some-other-package
malthe
la source
préférable d'utiliserinotify
ctrl-alt-delor
3

J'ai fait un script qui fait ceci:

#!/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

Enregistrez ce qui précède dans /usr/local/sbin/apt-get. apt-getattendra ensuite si une autre instance est déjà en cours d'exécution.

Sinon, enregistrez-le sous un /usr/local/sbin/apt-waitexemple d'utilisation:

apt-wait && aptitude

qui s'exécutera aptitudeaprès la fin du processus en cours contenant le verrou.

Exemple d'exécution:

  1. Tout d'abord, une apt-getcommande est exécutée, par exemple:

    $ sudo apt-get remove some_package
    
  2. Ensuite, dans un autre terminal, une autre commande est exécutée:

    $ sudo apt-get install some_other_package
    

    Il attendra que la première commande se termine puis s’exécute. Sortie en attendant:

    [/] Waiting for other package managers to finish...
          28223 : /usr/bin/apt-get remove some_package
    
kiri
la source
1
préférable d'utiliserinotify
ctrl-alt-delor
3

Malheureusement, fuser ne fait pas grand chose pour vous lorsque vous utilisez plusieurs conteneurs d’espace de noms non privilégiés comme lxc.

De plus, aptdcon n’est pas installé par défaut (au moins le 18.04) et met votre tâche en arrière-plan dans une file d’attente de sorte que vous perdiez la sérialisation. Ce n'est pas insurmontable, mais cela signifie que votre automatisation doit avoir un moyen d'éviter les erreurs de flock dans apt lors de l'installation d'aptdcon, et vous aurez besoin d'une sorte de boucle d'attente pour tout ce que vous devez sérialiser après avoir installé des paquets via aptdcon. à moins qu'il y ait une sorte de drapeau pour cela déjà.

Qu'est-ce que le travail est troupeau. Cela devrait également fonctionner sur NFS, etc., car il utilise le verrouillage du système de fichiers de la même façon qu'apt, mais avec le paramètre -w secondes, il attendra sur votre verrou au lieu de générer une erreur.

Donc, en suivant le modèle du wrapper, ajoutez ceci comme apt-get dans / usr / local / bin / et partagez-le.

Cela présente également l’avantage de limiter les entrées-sorties en interdisant le parallélisme sur apt, de sorte que vous pouvez laisser cron déclencher les mises à jour à minuit partout sans frapper le disque.

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

Une demande de fonctionnalité très simple et agréable pour apt-get serait un drapeau -w permettant de basculer vers un verrou bloquant / en attente.

Raymond Ferguson
la source
apt utilise fcntl, pas flock, donc cela ne fonctionnera pas. askubuntu.com/questions/1077215/…
Andy
Cela fonctionne parfaitement et constitue une meilleure option que d'utiliser fusercomme indiqué dans de nombreuses réponses, car il fuserreste sujet à des conditions de concurrence féroce entre l'observation du verrou et le processus cible qui saisit le verrou à nouveau. Avec flockle verrou est acquis et ensuite passé au processus cible afin qu'il ne laisse pas de temps pour perdre le verrou dans l'intervalle.
jrudolph
1

One-Liner basé sur la réponse d'Oli :

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