668242c422
Change-Id: Ia968ec7375ab346a2155769a46e74ce694a57fc2
202 lines
5.3 KiB
Python
202 lines
5.3 KiB
Python
# Copyright (c) 2014 OpenStack Foundation
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
# implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
'''
|
|
Bindings to the `tee` and `splice` system calls
|
|
'''
|
|
|
|
import os
|
|
import operator
|
|
import six
|
|
import ctypes
|
|
import ctypes.util
|
|
|
|
__all__ = ['tee', 'splice']
|
|
|
|
|
|
c_loff_t = ctypes.c_long
|
|
|
|
|
|
class Tee(object):
|
|
'''Binding to `tee`'''
|
|
|
|
__slots__ = '_c_tee',
|
|
|
|
def __init__(self):
|
|
libc = ctypes.CDLL(ctypes.util.find_library('c'), use_errno=True)
|
|
|
|
try:
|
|
c_tee = libc.tee
|
|
except AttributeError:
|
|
self._c_tee = None
|
|
return
|
|
|
|
c_tee.argtypes = [
|
|
ctypes.c_int,
|
|
ctypes.c_int,
|
|
ctypes.c_size_t,
|
|
ctypes.c_uint
|
|
]
|
|
|
|
c_tee.restype = ctypes.c_ssize_t
|
|
|
|
def errcheck(result, func, arguments):
|
|
if result == -1:
|
|
errno = ctypes.set_errno(0)
|
|
|
|
raise IOError(errno, 'tee: %s' % os.strerror(errno))
|
|
else:
|
|
return result
|
|
|
|
c_tee.errcheck = errcheck
|
|
|
|
self._c_tee = c_tee
|
|
|
|
def __call__(self, fd_in, fd_out, len_, flags):
|
|
'''See `man 2 tee`
|
|
|
|
File-descriptors can be file-like objects with a `fileno` method, or
|
|
integers.
|
|
|
|
Flags can be an integer value, or a list of flags (exposed on
|
|
`splice`).
|
|
|
|
This function returns the number of bytes transferred (i.e. the actual
|
|
result of the call to `tee`).
|
|
|
|
Upon other errors, an `IOError` is raised with the proper `errno` set.
|
|
'''
|
|
|
|
if not self.available:
|
|
raise EnvironmentError('tee not available')
|
|
|
|
if not isinstance(flags, six.integer_types):
|
|
c_flags = six.moves.reduce(operator.or_, flags, 0)
|
|
else:
|
|
c_flags = flags
|
|
|
|
c_fd_in = getattr(fd_in, 'fileno', lambda: fd_in)()
|
|
c_fd_out = getattr(fd_out, 'fileno', lambda: fd_out)()
|
|
|
|
return self._c_tee(c_fd_in, c_fd_out, len_, c_flags)
|
|
|
|
@property
|
|
def available(self):
|
|
'''Availability of `tee`'''
|
|
|
|
return self._c_tee is not None
|
|
|
|
|
|
tee = Tee()
|
|
del Tee
|
|
|
|
|
|
class Splice(object):
|
|
'''Binding to `splice`'''
|
|
|
|
# From `bits/fcntl-linux.h`
|
|
SPLICE_F_MOVE = 1
|
|
SPLICE_F_NONBLOCK = 2
|
|
SPLICE_F_MORE = 4
|
|
SPLICE_F_GIFT = 8
|
|
|
|
__slots__ = '_c_splice',
|
|
|
|
def __init__(self):
|
|
libc = ctypes.CDLL(ctypes.util.find_library('c'), use_errno=True)
|
|
|
|
try:
|
|
c_splice = libc.splice
|
|
except AttributeError:
|
|
self._c_splice = None
|
|
return
|
|
|
|
c_loff_t_p = ctypes.POINTER(c_loff_t)
|
|
|
|
c_splice.argtypes = [
|
|
ctypes.c_int, c_loff_t_p,
|
|
ctypes.c_int, c_loff_t_p,
|
|
ctypes.c_size_t,
|
|
ctypes.c_uint
|
|
]
|
|
|
|
c_splice.restype = ctypes.c_ssize_t
|
|
|
|
def errcheck(result, func, arguments):
|
|
if result == -1:
|
|
errno = ctypes.set_errno(0)
|
|
|
|
raise IOError(errno, 'splice: %s' % os.strerror(errno))
|
|
else:
|
|
off_in = arguments[1]
|
|
off_out = arguments[3]
|
|
|
|
return (
|
|
result,
|
|
off_in.contents.value if off_in is not None else None,
|
|
off_out.contents.value if off_out is not None else None)
|
|
|
|
c_splice.errcheck = errcheck
|
|
|
|
self._c_splice = c_splice
|
|
|
|
def __call__(self, fd_in, off_in, fd_out, off_out, len_, flags):
|
|
'''See `man 2 splice`
|
|
|
|
File-descriptors can be file-like objects with a `fileno` method, or
|
|
integers.
|
|
|
|
Flags can be an integer value, or a list of flags (exposed on this
|
|
object).
|
|
|
|
Returns a tuple of the result of the `splice` call, the output value of
|
|
`off_in` and the output value of `off_out` (or `None` for any of these
|
|
output values, if applicable).
|
|
|
|
Upon other errors, an `IOError` is raised with the proper `errno` set.
|
|
|
|
Note: if you want to pass `NULL` as value for `off_in` or `off_out` to
|
|
the system call, you must pass `None`, *not* 0!
|
|
'''
|
|
|
|
if not self.available:
|
|
raise EnvironmentError('splice not available')
|
|
|
|
if not isinstance(flags, six.integer_types):
|
|
c_flags = six.moves.reduce(operator.or_, flags, 0)
|
|
else:
|
|
c_flags = flags
|
|
|
|
c_fd_in = getattr(fd_in, 'fileno', lambda: fd_in)()
|
|
c_fd_out = getattr(fd_out, 'fileno', lambda: fd_out)()
|
|
|
|
c_off_in = \
|
|
ctypes.pointer(c_loff_t(off_in)) if off_in is not None else None
|
|
c_off_out = \
|
|
ctypes.pointer(c_loff_t(off_out)) if off_out is not None else None
|
|
|
|
return self._c_splice(
|
|
c_fd_in, c_off_in, c_fd_out, c_off_out, len_, c_flags)
|
|
|
|
@property
|
|
def available(self):
|
|
'''Availability of `splice`'''
|
|
|
|
return self._c_splice is not None
|
|
|
|
|
|
splice = Splice()
|
|
del Splice
|