Cómo leer un descriptor de archivo abierto desde fuera del proceso de escritura

Cómo leer un descriptor de archivo abierto desde fuera del proceso de escritura

¿Cómo se abre un descriptor de archivo y se hace eco en un terminal mientras se escribe en él desde un proceso?

Tengo un programa de respaldo, Duplicity, que escribe sus registros en un descriptor de archivo especificado por el --log-fd=16parámetro.

Efectivamente, si corro lsof -p <duplicity PID>veo:

python2 9224 myuser    0r      CHR                1,3      0t0         6 /dev/null
python2 9224 myuser    1w      CHR                1,3      0t0         6 /dev/null
python2 9224 myuser    2w      CHR                1,3      0t0         6 /dev/null
python2 9224 myuser    3u  a_inode               0,11        0      7005 [eventfd]
python2 9224 myuser    4u     unix 0x0000000000000000      0t0    158199 type=STREAM
python2 9224 myuser    5u  a_inode               0,11        0      7005 [eventfd]
python2 9224 myuser    6u  a_inode               0,11        0      7005 [eventfd]
python2 9224 myuser    7r      DIR                8,3     4096  22414346 <some random file being accessed during the backup>
python2 9224 myuser    8r      CHR                1,9      0t0        11 /dev/urandom
python2 9224 myuser   15r     FIFO               0,10      0t0    157054 pipe
python2 9224 myuser   16w     FIFO               0,10      0t0    157054 pipe

Sin embargo, si intento abrir el descriptor de archivo en Python, aparece un error:

>>> import os
>>> os.fdopen(16)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OSError: [Errno 9] Bad file descriptor

¿Por qué es esto? ¿Cómo leo el descriptor del archivo?

Respuesta1

Uso strace(rastreo de llamadas y señales del sistema).

Uso:

sudo strace -p <PID of writing process> -s 9999 -e write=<corresponding FD>

Desde la página del manual:

       -p pid      Attach to the process with the process ID pid and begin tracing.  The trace may be terminated
                   at any time by a keyboard interrupt signal (CTRL-C).  strace will respond by detaching itself
                   from  the  traced process(es) leaving it (them) to continue running.  Multiple -p options can
                   be used to attach to many processes in addition to command (which is optional if at least one
                   -p option is given).  -p "`pidof PROG`" syntax is supported.
    
       -s strsize  Specify the maximum string size to print (the default is 32).  Note that  filenames  are  not
                   considered strings and are always printed in full.
    
       -e read=set
              Perform a full hexadecimal and ASCII dump of all the data read from file descriptors listed in the
              specified set.  For example,  to  see  all  input  activity  on  file  descriptors  3  and  5  use
              -e read=3,5.   Note  that  this  is independent from the normal tracing of the read(2) system call
              which is controlled by the option -e trace=read.

       -e write=set
              Perform a full hexadecimal and ASCII dump of all the data written to file  descriptors  listed  in
              the  specified  set.   For  example,  to  see  all output activity on file descriptors 3 and 5 use
              -e write=3,5.  Note that this is independent from the normal tracing of the write(2)  system  call
              which is controlled by the option -e trace=write.

Árbitro:https://man7.org/linux/man-pages/man1/strace.1.html

Respuesta2

Creo que --log=fdla opción de duplicidad está destinada a canalizaciones complejas en las que desea separarse stderrde stdoutsu registro.

Esta respuesta aesta preguntada un ejemplo. He aquí un ejemplo sencillo:

#!/bin/sh
# Generate output on three different fds
echo hello >&3
echo world >&2
echo today >&1

Y cuando se ejecuta así,

./foo 2> 2.log 3> 3.log 1> 1.log

Resultados en

$ cat 1.log 2.log 3.log
today
world
hello

Respuesta3

Linux obtuvo recientemente llamadas al sistema para exactamente este tipo de cosas:

  1. Usarpidfd_openpara obtener un "PID FD" de un PID.

  2. Usarpidfd_getfdpara obtener un descriptor de archivo de otro proceso a través de su PID FD.

A partir de Python 3.9, pidfd_openestá disponible comoos.pidfd_open.

pidfd_getfdaún no está expuesto a través de la biblioteca estándar de Python, pero afortunadamentectypesllamemossyscall, los números de llamada del sistema Linux nunca cambian, y la API y ABI de llamadas del sistema Linux solo cambian de manera compatible con versiones anteriores.

¡Entonces!

from ctypes import CDLL, c_int, c_long, c_uint, get_errno
from functools import partial
from os import strerror


_syscall = CDLL(None, use_errno=True).syscall

# Non-variadic system call number argument:
_syscall.argtypes = [c_long]


def pidfd_getfd(pidfd, targetfd):
    fd = _syscall(
             438,  # system call number of pidfd_getfd
             c_int(pidfd),
             c_int(targetfd),
             c_uint(0),  # unused "flags" argument
         )
    if fd == -1:
        errno = get_errno()
        raise OSError(errno, strerror(errno))
    return fd

Entonces, en su ejemplo, donde el PID de interés es 9224, en lugar de llamar a os.fdopen(16), haría os.fdopen(pidfd_getfd(os.pidfd_open(9224), 16)).

Tenga en cuenta que esto solo funciona si tiene los permisos necesarios para acceder al proceso de destino, por lo que es posible que deba ejecutar este código con privilegios elevados (por ejemplo sudo), dependiendo de cómo se inició el proceso y cómo está configurado su sistema.

información relacionada