37

I want to make syscall in Python and the function is not in libc, is there a way to do it in Python?

More specifically, I want to call getdents, whose manpage says

Note: There are no glibc wrappers for these system calls;

All existing related solutions I found on the web uses ctypes with libc.so: for example.

Please don't question why I want to use getdents directly, I have a very specific reason to do that, and it would be distracting to discuss in this question. Thank you.

1 Answer 1

46

Libc exposes a function to invoke "custom" syscalls: long syscall(long number, ...);

syscall() is a small library function that invokes the system call whose assembly language interface has the specified number with the specified arguments. Employing syscall() is useful, for example, when invoking a system call that has no wrapper function in the C library.

Just access this function like any foreign function:

import ctypes

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

e.g.

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

Or to translate the example in the man page:

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
Sign up to request clarification or add additional context in comments.

2 Comments

Is this strategy _getdents = ctypes.CDLL(None).syscall reusable. Let's say you wanted both getdents and some other syscall that takes a different set of arguments. Can you create another copy of syscall and then modify argtypes to something else? Or does anotherfun = ctypes.CDLL(None).syscall refer to the same object as _getdents and so modifying argtypes also modifies argtypes of _getdents?
@cheshirekow What a strange coincidence... I was just looking that up myself now, 10 hours after you asked it on a 3 year old question... AFAICT the answer is that for a single call to CDLL you can only set the argtypes once. But if you call CDLL(None) twice, you can set dll.syscall.argtypes separately on each copy.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.