web-dev-qa-db-fra.com

Faire syscall dans Python

Je veux faire syscall en Python et la fonction n'est pas en libc, est-il possible de le faire en Python?

Plus précisément, je veux appeler getdents , dont la page de manuel indique

Remarque: Il n'y a pas d'encapsuleurs glibc pour ces appels système;

Toutes les solutions connexes existantes que j'ai trouvées sur le Web utilisent ctypes avec libc.so: pour exemple .

Veuillez ne pas vous demander pourquoi je veux utiliser getdents directement, j'ai une raison très spécifique de le faire, et ce serait gênant d'en discuter dans cette question. Je vous remercie.

18
Kan Li

Libc expose une fonction pour invoquer des appels système "personnalisés": long syscall(long number, ...);

syscall() est une petite fonction de bibliothèque qui appelle l'appel système dont l'interface du langage d'assembly a le number spécifié avec les arguments spécifiés. L'utilisation de syscall() est utile, par exemple, lors de l'appel d'un appel système qui n'a pas de fonction wrapper dans la bibliothèque C.

Accédez simplement à cette fonction comme n'importe quelle fonction étrangère:

import ctypes

libc = ctypes.CDLL(None)
syscall = libc.syscall

par exemple.

syscall(39)  # 39 = getpid, but you get the Gist

Ou pour traduire l'exemple dans la page de manuel:

import os, ctypes

off_t = ctypes.c_long  # YMMV
__NR_getdents = 78  # YMMV

class linux_dirent(ctypes.Structure):
    _fields_ = [
        ('d_ino', ctypes.c_long),
        ('d_off', off_t),
        ('d_reclen', ctypes.c_ushort),
        ('d_name', ctypes.c_char)
    ]

_getdents = ctypes.CDLL(None).syscall
_getdents.restype = ctypes.c_int
_getdents.argtypes = ctypes.c_long, ctypes.c_uint, ctypes.POINTER(ctypes.c_char), ctypes.c_uint

fd = os.open('/tmp/', os.O_RDONLY | os.O_DIRECTORY)

buf = ctypes.ARRAY(ctypes.c_char, 1024)()
while True:
    nread = _getdents(__NR_getdents, fd, buf, len(buf))
    if nread == -1:
        raise OSError('getdents')
    Elif nread == 0:
        break

    pos = 0
    while pos < nread:
        d = linux_dirent.from_buffer(buf, pos)

        name = buf[pos + linux_dirent.d_name.offset : pos + d.d_reclen]
        name = name[:name.index('\0')]
        print 'name:', name

        pos += d.d_reclen
23