Forum Programmation.autre traquer un process par son pid

Posté par  (site web personnel) . Licence CC By‑SA.
Étiquettes : aucune
0
19
oct.
2023

Bonjour, je développe un programme (en Rust mais sans importance) qui a besoin de traquer certains événements liés à un pid:
- création d'un child process
- fin du programme (légitime ou kill ou autre)
- la création de sockets (INET) connectés

Mon soft utilisant déjà netlink, j'ai testé avec netlink-audit mais ce n'est pas concluant:
- quand le daemon auditd est démarré, mon soft ne reçoit plus les events [auditd est stoppé pour la suite]
- pour la fin de vie du programme, écouter le syscall exit_group fait l'affaire mais je n'intercepte pas les kill
- manque d'infos complémentaires liés à chaque event

Je pourrai forcer en ajoutant l'écoute du syscall kill, mais il m'en manquera certainement encore, je ne peux pas tester tous les cas de figure (par exemple avec de vms/containers démarrés).
Et le plus important, les events reçus ne me fournissent pas assez d'informations, j'ai besoin de récupérer les pid enfants, les sockets locales utilisées…

Sachant que toutes solutions à base de scan de /proc, /sys ne me conviennent pas, il me reste comme idée d'utiliser ebpf sur les kprobe/kretprobe (ps: mon soft utilise déjà ebpf pour autre chose).

As-tu idée d'une (ou plusieurs) autre manière de résoudre mon problème ?

  • # Ça s'appelle ptrace(2), non ?

    Posté par  (site web personnel) . Évalué à 4. Dernière modification le 19 octobre 2023 à 15:36.

    Voir également strace(1).

    Debian Consultant @ DEBAMAX

    • [^] # Re: Ça s'appelle ptrace(2), non ?

      Posté par  (site web personnel) . Évalué à 1.

      ptrace me fournira le même genre d'infos que netlink-audit mais ça risque d'être plus "lourd" (je dois pouvoir réagir aux events d'un nombre indéterminé de process).

      Sinon, j'ai utilisé strace pour inspecter les requêtes netlink-route faites par ip link/addr pour m'en inspirer.

  • # pidof ?

    Posté par  . Évalué à 4.

    en shell on a pidof pour connaitre le PID d'un process
    tu as aussi la solution de poser un /var/run/TONPROCESS.pid
    et de le virer quand le process a finit

    ainsi celui qui surveiller a juste à verifier que /var/run/TONPROCESS.pid existe, et il obtient le PID du process ou le lances si necessaire

    • [^] # Re: pidof ?

      Posté par  (site web personnel) . Évalué à 1.

      Je connais les PID des processus à monitorer.

      Je veux réagir au plus vite à la création de leur descendance, récupérer les PIDs des childs pour les monitorer eux aussi.
      Puis, je dois lister tous les 4-tuples des sockets "connectées" (syscall accept ou connect) et réagir à la création de nouveaux 4-tuples sockets de ces pids (je gère déjà la fin de vie des 4-tuples grâce à netlink sock_diag).

      En détails, mon programme est un limiteur de bande passante à la trickle, mais il permet de limiter des process (à démarrer ou déjà démarrés), des cgroup (v2) ou des sockets, les grouper et surtout modifier les limites. La partie limiteur est assurée par des qdisc sur 2 ifb, le match des packets réseau est fait par du code ebpf et le redirect vers l'ifb concerné avec un filtre tc.

      • [^] # Re: pidof ?

        Posté par  (site web personnel, Mastodon) . Évalué à 3.

        Toujours en shell, tu peux récupérer les processus fils d’un autre dont tu connais le PID avec :

        • pgrep -P $pid dans toutes les distributions GNU/Linux qui se respectent
        • ps --ppid $pid -o pid avec l’implémentation GNU de ps
        • ps -x -o pid,ppid | awk -v=$pid '$1==v {print $2}' avec n’importe quel ps
        • pstree -p $pid va par contre procéder récursivement et afficher en ascii-art, du coup il faut enchainer avec quelque chose comme | tr '\n' ' ' | sed 's/[^0-9]/ /g' | sed 's/[[:space:]][[:space:]]*/ /g'
        • cat /proc/$pid/task/*/children pour les distributions GNU/Linux

        Mais je ne sais pas si c’est pertinent vu que tu vas faire le boulot en Rust.

        “It is seldom that liberty of any kind is lost all at once.” ― David Hume

        • [^] # Re: pidof ?

          Posté par  (site web personnel) . Évalué à 1.

          Utilisant ebpf, mon outil ne sera donc que Linux only.
          Je ne sais pas si les sockets netlink existent sur les autres noyaux.

          J'ai regardé le fonctionnement de tous les outils que j'ai pu trouvé, ils utilisent tous /proc pour récupérer les informations.
          Je ne trouve pas ça très élégant (voir même peu performant), mais je vais devoir faire la même chose pour trouver les fils et les sockets déjà connectées.
          Pour les events, ce sera programmes et maps ebpf.

          • [^] # Re: pidof ?

            Posté par  (site web personnel, Mastodon) . Évalué à 2.

            En effet, procps et procps-ng (ce dernier a déménagé sur GL), autour desquels ces outils s’articulent, utilisent /proc/ Mais attention que c’est un système de fichier virtuel et que les outils comme ps ne font pas comme l’usager que je suis ferait avec cat
            Ailleurs, je vois par exemple que le ps de FreeBSD fait appel à : <sys/proc.h>, <sys/stat.h>, <sys/queue.h>, etc. Cette distribution connait aussi les sockets netlink, mais effectivement ça ne semble pas être le cas partout.
            Ceci dit, à moins d’être super bilingue en C et en Rust, ce n’est pas toujours évident. J’ai cependant trouvé ceci qui pourrait te donner de bonnes pistes.

            “It is seldom that liberty of any kind is lost all at once.” ― David Hume

            • [^] # Re: pidof ?

              Posté par  (site web personnel) . Évalué à 2.

              Pour le dernier lien, ça utilise la crate procfs quand utilisé sous Linux.
              J'avais déjà regardé, ça parse les entrées de /proc.

              Quand je dis que je ne trouve pas cette solution "élégante", c'est parce que ça fait des read() (et autres accès fs).
              Bien que /proc soit un répertoire "spécial", ça reste moins pratique qu'une solution (apparemment inexistante) qui ferait une seule requête au noyau ("quels sont tous les descendants du Pid 1234").

              De plus, parser /proc ne retourne pas un instantané: le temps de lister et parser les infos des childs, un nouveau aurait pu apparaître.
              Du coup, lorsque je reçoit un Pid à monitorer, je dois d'abord activer le code ebpf pour ce Pid (traquer les fork, clone ..) puis parser /proc.

              • [^] # Re: pidof ?

                Posté par  (site web personnel, Mastodon) . Évalué à 3.

                Quand je dis que je ne trouve pas cette solution "élégante", c'est parce que ça fait des read() (et autres accès fs).
                Bien que /proc soit un répertoire "spécial", ça reste moins pratique qu'une solution (apparemment inexistante) qui ferait une seule requête au noyau ("quels sont tous les descendants du Pid 1234").

                Cela me fait penser à cette discussion https://news.ycombinator.com/item?id=38009372 (ça n’apporte pas de solution mais j’ai trouvé les commentaires intéressants.)

                “It is seldom that liberty of any kind is lost all at once.” ― David Hume

                • [^] # Re: pidof ?

                  Posté par  (site web personnel) . Évalué à 2.

                  Effectivement, la première partie des commentaires est intéressante.
                  Je crois qu'il ne me reste plus qu'à espérer des ajouts à netlink.

  • # Prendre l'inspiration dans les sources de systemd ?

    Posté par  (site web personnel) . Évalué à 2.

    C'est exactement ce que fait systemd.
    Tu pourrais regarder comment ils font.

    Il me semble qu'il y a un nouvel appel noyau en préparation pour éviter une race condition ou le pid d'un process est immédiatement réutiliser

    • [^] # Re: Prendre l'inspiration dans les sources de systemd ?

      Posté par  (site web personnel) . Évalué à 1.

      J'ai commencé à fouiller un peu, mais j'ai l'impression que systemd utilise beaucoup les cgroup .

      Une option intéressante, et ajoutée par les dev de systemd au noyau, est l'option PR_SET_CHILD_SUBREAPER pour prctl():
      avec cette option, un processus père peut récupérer ses "petits enfants" à la place du pid 1 quand ses childs se terminent.

      Je ne peux pas utiliser cette option, car je fonctionne en mode client/serveur avec une socket UNIX.
      Le client vérifie les options de la commande et démarrera les commandes à limiter si besoin, et on peut limiter des process déjà démarrés.
      Il envoie la requête au daemon, attend la réponse et se termine.
      Le daemon se charge de configurer le système, il ne reçoit que les pid à limiter.

      Les commandes d'admin habituelles (ss, netstat, lsof, ps, ..) utilisent toutes /proc, il n'y a peut être pas d'autres solutions.

Suivre le flux des commentaires

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