Forum Programmation.shell Lancer une application sur un TTY distinct depuis un démon

Posté par  . Licence CC By‑SA.
Étiquettes :
3
30
jan.
2013

Bonjour,

J'ai une tâche en fond (un démon sous utilisateur root) qui vérifie à intervalle périodique un événement. Lorsque cet événement se produit, je veux que l'utilisateur physiquement connecté sur la machine soit averti de cet événement et qu'il lui soit soumis une fenêtre de dialogue lui permettant de choisir la suite des actions.

Lorsque que l'événement se déclenche, le démon me permet d'exécuter des scripts shell. Le fonctionnement de mon script actuel est le suivant : tout d'abord, récupérer quel terminal VT est actif grâce à fgconsole. Ensuite, identifier le type de terminal qu'il s'agisse d'une session X ou d'une console TTY grâce à who. Une fois identifié, lancer ma fenêtre de dialogue (construite avec zenity sous X ou dialog en console), récupérer les valeurs de retour et agir en conséquence.

Sous X, je n'ai aucune problème. Mon serveur autorise les interventions externes avec xhost, je récupère le n° de serveur X, et je lance ma commande avec la syntaxe suivante : DISPLAY=:$display zenity --list .... La boîte s'affiche bien, attend ma réaction et se ferme normalement en ayant laissé les valeurs de retour.

Sous la console TTY, je n'y arrive pas. En supposant qu'un utilisateur lamba ait ouvert une session sur /dev/tty1 (ce sera toujours le cas), voilà ce que j'ai essayé:
- rediriger les flux STDIN et STDOUT : dialog --menu ... 0</dev/tty1 1</dev/tty1 : ça ne marche que très mal, l'affichage n'est pas complet et la saisie est erratique;
- ouvrir une session screen/tmux et utiliser le terminal virtuel : dialog --menu ... 0</dev/pts/0 1</dev/pts/0 : idem;
- utiliser openvt : openvt -c $vt dialog --menu ... : impossible, openvt exige que le VT soit totalement disponible, hors getty est actif, les deux s'excluent;
- chvt : ne fait que passer d'un terminal à un autre.

Le seul début de solution trouvé, c'est writevt qui permet d'écrire des caractères sur un TTY donné (en gros, une simulation de frappe clavier). Je l'utilise comme pour simuler le lancement par l'utilisateur lamba de la boîte de dialogue et c'est ce que je veux. Le problème, c'est qu'en faisant par exemple echo Debut ; writevt -t /dev/tty1 -T dialog --menu ... ; echo Fin, le script n'attend pas que dialog se soit fermé puisque writevt a déjà renvoyé sa valeur de retour (il n'attend pas de retour du sous-programme lancé puisque ce n'est pas nécessairement une commande) et du coup, je ne peux rien récupérer.

Actuellement, je pense faire en deux temps avec 2 sripts séparés:
1. le premier avec writevt, je lance d'abord dialog qui enregistre sa valeur de retour dans un fichier /tmp/truc, et comme ce script n'attend pas le retour comme énoncé auparavant, il se termine tout de suite;
2. le 2e sera lancé en même temps et se contentera de tourner en boucle et de vérifier régulièrement la création d'un fichier /tmp/truc, puis une fois détecté, lira le retour dans ce fichier et lancera les actions.

Qu'en pensez-vous? Cela me semble trop bidouillage pour ce que je veux faire, mais Google et moi sommes à court d'idée…

  • # tty non graphique = pas "multitache", quelques idées quand meme

    Posté par  . Évalué à 6.

    ton probleme vient du fait que TTY en non graphique n'est pas multitache.

    or, dans ton cas, il faudrait :
    1°) suspendre ce que l 'utilisateur est en train de faire,
    2°) afficher l'information et la boite de dialogue, valider les choix, etc, fermer la boite de dialog
    3°) reprendre ce que l'utilisateur etait en train de faire

    • [^] # Re: tty non graphique = pas "multitache", quelques idées quand meme

      Posté par  . Évalué à 1.

      C'est vrai, j'avais pensé à ce cas. Pour l'instant, on présume que la console est ouverte, mais n'exécute rien de bloquant - j'y vais étape par étape. Au pire, par le futur, je peux toujours essayer de suspendre le process en cours et le ramener en premier plan une fois le dialogue fermé.

      • [^] # Re: tty non graphique = pas "multitache", quelques idées quand meme

        Posté par  . Évalué à 2.

        si la console est ouverte, le programme login ou bash est en train de tourner.

        sinon tu peux regarder comment wall ou talk fonctionne

        • le premier permet de diffuser un message sur tous les TTY (par ex quand la machine reboot ou s'arrete)
        • le deuxieme permettait (je parle au passé car j'ai pas testé recemment) de dialoguer entre 2 utilisateurs (2tty)
      • [^] # Re: tty non graphique = pas "multitache", quelques idées quand meme

        Posté par  . Évalué à 3.

        Une piste : intercepter un signal dans ton bash_profile. Par contre l'inconvénient est que le shell attend la fin de l'instruction en cours pour exécuter le handler, ce qui signifie que si un shell lance un sous-shell, il faudra attendre la fin de celui-ci pour que le signal soit récupéré et que l'action correspondante soit réalisée.

        Sinon, il faudrait réécrire getty pour qu'il puisse intercepter le signal et agir en conséquences (ou trouver un tty qui sache le faire).

        action1() {
        echo "Reisation de l'action 1"
        }
        action2() {
        echo "Reisation de l'action 2"
        }

        signal_handler() {
        echo "Vous avez recu un signal."
        echo "Evenement XXXX"
        echo "Que voulez-vous faire?"
        echo " - choix 1 : action 1"
        echo " - choix 2 : action 2"
        read rep
        case $rep in
        1)
        action1
         ;;
        2) action2
         ;;
        *)
        echo "Choix inconnu"
         ;;
        esac
        echo "Retour a la normale"
        }

        trap signal_handler SIGUSR1

        • [^] # Re: tty non graphique = pas "multitache", quelques idées quand meme

          Posté par  . Évalué à 3.

          Pourquoi ne pas utiliser le signal SIGSTOP pour suspendre le shell ?

          L'envoi de SIGSTOP au shell le suspend immédiatement et ne peut pas être intercepté ou ignoré.
          Une fois les interactions avec le terminal terminées, il suffit de réveiller le shell avec SIGCONT.

          Au cas où on veut suspendre toute activité dans le tty même si le shell a lancé d'autres programmes, on peut même suspendre tous les process rattachés au shell:
          en supposant que $shellpid est le PID du shell à suspendre, la commande suivante suspend le groupe de process:

          kill -STOP -$shellpid
          
          

          et

          kill -CONT -$shellpid
          
          

          relance tout.

          • [^] # Re: tty non graphique = pas "multitache", quelques idées quand meme

            Posté par  . Évalué à 0.

            C'est également une solution que je n'avais pas envisagé, merci. Je peux effectivement pauser mon script après avoir lancé le dialogue, mais hélas, cela implique de savoir quand/comment le faire continuer, et comme le script est lancé par root, si j'ai un utilisateur lambda, il ne pourra pas lui dire de reprendre en raison des droits sur le processus. Après, je peux aussi manipuler les droits sur le processus, ou faire reprendre le script automatiquement au bout de x secondes.

            En tout cas, cela confirme ma pensée que ce que je cherche à faire n'est pas aussi simple que prévu…

            • [^] # Re: tty non graphique = pas "multitache", quelques idées quand meme

              Posté par  . Évalué à 3.

              Avant de faire joli, tu peux essayer de faire simple:
              une fois que ton script a suspendu le shell associé au tty, il a un accès exclusif et peut simplement afficher des informations avec echo et lire les réponses avec read.

              Une fois que ça marchera, tu peux utiliser la commande dialog pour gérer le dialogue avec l'utilisateur.
              En simplifiant, ça donne:

              kill -STOP -$shellpid
              dialog --checklist titre 10 20 2 tag1 item1 status1 tag2 item2 status2 < /dev/pts/0 > /dev/pts/0 2>/tmp/reponse
              # Code pour gérer le choix utilisateur stocké dans /tmp/reponse
              kill -CONT -$shellpid
              
              
  • # Redirections bizarres

    Posté par  . Évalué à 2.

    Bien que ça n'ait pas un lien direct avec la question posée, tu utilise une syntaxe bizarre pour les redirections:

    0</dev/tty1 1</dev/tty1
    
    

    Le symbole < indique une redirection en lecture et > une redirection en écriture.
    La syntaxe correcte serait:

    0</dev/tty1 1>/dev/tty1
    
    

    De plus, 0 et 1 sont les valeurs par défaut quand aucun numéro de fichier n'est spécifié. On peut donc simplifier en:

    </dev/tty1 >/dev/tty1
    
    
    • [^] # Re: Redirections bizarres

      Posté par  . Évalué à 0.

      Explication simple : je me suis planté de sens lorsqu'ai j'ai écrit ce poste. Il fallait bien lire 1>/dev/tty1. Évidemment, ça ne marche pas pour autant… Mes excuses.

      • [^] # Re: Redirections bizarres

        Posté par  . Évalué à 2.

        Le fait que ton programme soit en compétition avec le shell pour interagir avec le terminal est certainement la source de tes problèmes.
        Voir les solutions préconisées dans les commentaires précédents pour avoir un accès exclusif au terminal.

  • # Ma solution

    Posté par  . Évalué à 1.

    J'ai trouvé une solution. Comme souvent conseillé, j'ai repensé ma manière de faire plutôt que de m'embarquer dans un montage trop difficile, avec writevt ou les signaux. Je l'écris ici en espérant que ça puisse servir à qqn d'autre…

    En fait, je suis repassé avec openvt. Mon script fait un appel à openvt sur un VT non encore initialisé avec getty, du genre tty8. Avec l'option -s, le basculement se fait automatiquement sur le nouveau tty qui affiche ma boîte de dialogue. Le résultat de cette boîte est enregistré dans un fichier temporaire.

    Comme openvt se ferme immédiatement sans attendre le retour de dialog, je mets une boucle while () juste après qui regarde toutes les secondes si le fichier temporaire en question existe bien et n'est pas vide. Une fois fait, je récupère la valeur de retour contennue dans le fichier temporaire, je rebascule avec chvt sur le tty originel et je désalloue avec deallocvt le tty dont je n'ai plus besoin.

    En fait, j'étais trop obstiné à vouloir utiliser une session déjà existante. Avec cette solution, même si je n'ai pas de session ouverte, je peux quand même interagir. Voilà.

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.