Como ler um descritor de arquivo aberto fora do processo de gravação

Como ler um descritor de arquivo aberto fora do processo de gravação

Como você abre um descritor de arquivo e o ecoa em um terminal enquanto ele está sendo gravado em um processo?

Eu tenho um programa de backup, Duplicity, que grava seus logs em um descritor de arquivo especificado pelo --log-fd=16parâmetro.

Com certeza, se eu correr, lsof -p <duplicity PID>vejo:

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

No entanto, se eu tentar abrir o descritor de arquivo em Python, recebo um erro:

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

Por que é isso? Como leio o descritor de arquivo?

Responder1

Use strace(rastrear chamadas e sinais do sistema).

Uso:

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

Na página do 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.

Referência:https://man7.org/linux/man-pages/man1/strace.1.html

Responder2

Acredito que --log=fda opção de duplicidade se destina a pipelines complexos onde você deseja separar stderrseu stdoutlog.

Esta resposta paraessa questãodá um exemplo. Aqui está um exemplo simples:

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

E quando executado assim,

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

Resulta em

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

Responder3

O Linux recentemente ganhou chamadas de sistema exatamente para esse tipo de coisa:

  1. Usarpidfd_openpara obter um "PID FD" de um PID.

  2. Usarpidfd_getfdpara obter um descritor de arquivo de outro processo por meio de seu PID FD.

A partir do Python 3.9, pidfd_openestá disponível comoos.pidfd_open.

pidfd_getfdainda não está exposto na biblioteca padrão do Python, mas felizmentectypesvamos ligarsyscall, os números de chamada do sistema Linux nunca mudam, e a API e ABI de chamada do sistema Linux só mudam de maneira compatível com versões anteriores.

Então!

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

Portanto, no seu exemplo, onde o PID de interesse é 9224, em vez de ligar os.fdopen(16), você faria os.fdopen(pidfd_getfd(os.pidfd_open(9224), 16)).

Observe que isso só funciona se você tiver as permissões necessárias para acessar o processo de destino, portanto, talvez seja necessário executar esse código com privilégios elevados (por exemplo, sudo) dependendo de como o processo foi iniciado e como seu sistema está configurado.

informação relacionada