def _line_bulk_all_free(bulk: gpiod_line_bulk) -> bool: for it in bulk: if not gpiod_line_is_free(it): set_errno(EBUSY) return False return True
def check(): # Check that connection is alive buf = ctypes.create_string_buffer(2) try: sock_fd = self._socket.fileno() except socket.error as e: if e.errno == errno.EBADF: return errno.ECONNRESET else: if os.name == 'nt': flag = socket.MSG_PEEK self._socket.setblocking(False) else: flag = socket.MSG_DONTWAIT | socket.MSG_PEEK retbytes = self._sys_recv(sock_fd, buf, 1, flag) err = 0 if os.name != 'nt': err = ctypes.get_errno() else: err = ctypes.get_last_error() self._socket.setblocking(True) WWSAEWOULDBLOCK = 10035 if (retbytes < 0) and (err == errno.EAGAIN or err == errno.EWOULDBLOCK or err == WWSAEWOULDBLOCK): ctypes.set_errno(0) return errno.EAGAIN else: return errno.ECONNRESET
def cb_data(h, d): d is not None and d.append(h.nlmsg_type) if h.nlmsg_type == 0xff: ctypes.set_errno(errno.ENOBUFS) return mnl.MNL_CB_ERROR elif h.nlmsg_type == 0x7f: return mnl.MNL_CB_STOP else: return mnl.MNL_CB_OK
def _line_bulk_all_requested(bulk: gpiod_line_bulk) -> bool: for it in bulk: if not gpiod_line_is_requested(it): set_errno(EPERM) return False return True
def cb(attr, data): if not data: return mnl.MNL_CB_STOP if atype[0] != attr.nla_type: ctypes.set_errno(errno.EPERM) return mnl.MNL_CB_ERROR atype[0] += 1 return mnl.MNL_CB_OK
def gpiod_chip_get_line(chip: gpiod_chip, offset: int) -> Optional[gpiod_line]: """ @brief Get the handle to the GPIO line at given offset. @param chip: The GPIO chip object. @param offset: The offset of the GPIO line. @return The GPIO line handle or None if an error occured. """ if offset < 0 or offset >= chip.num_lines: set_errno(EINVAL) return None if chip.lines[offset] is None: line = gpiod_line(chip) line.fd_handle = None line.offset = offset chip.lines[offset] = line status = gpiod_line_update(chip.lines[offset]) if status < 0: return None return chip.lines[offset]
def gpiod_chip_open_by_label(label: str) -> Optional[gpiod_chip]: """ @brief Open a gpiochip by label. @param label: Label of the gpiochip to open. @return GPIO chip handle or None if the chip with given label was not found or an error occured. @note If the chip cannot be found but no other error occurred, errno is set to ENOENT. """ chip_iter = iter(gpiod_chip_iter()) if chip_iter is None: return None for chip in chip_iter: if chip.label == label: # gpiod_chip_iter_free_noclose return chip set_errno(ENOENT) # gpiod_chip_iter_free return None
def runner(argv: list = None, jobs: int = 0, ignored: list = None, flags: int = 0) -> None: """ Launch the PTEF runner logic, as implemented by ptef_runner(3). Note the lack of 'argc' - this python wrapper does len(argv) for you. Also note that 'argv' must contain the program name, or 'argv[0]', so it must always contain at least one element. If it is left None or empty, a default of 'sys.argv' is used. """ if not argv: # None or [] argv = [sys.argv[0]] argc = len(argv) argv_bstrings = (x.encode('utf-8') for x in argv) if not ignored: ignored = [] ignored_bstrings = [x.encode('utf-8') for x in ignored] ignored_bstrings.append(None) # NULL-terminate ctypes.set_errno(0) rc = libptef.ptef_runner( ctypes.c_int(argc), (ctypes.c_char_p * argc)(*argv_bstrings), ctypes.c_int(jobs), (ctypes.c_char_p * len(ignored_bstrings))(*ignored_bstrings), ctypes.c_int(flags)) if rc == -1: errno = ctypes.get_errno() raise OSError(errno, os.strerror(errno), None)
def gpiod_line_request_bulk( bulk: gpiod_line_bulk, config: gpiod_line_request_config, default_vals: List[int], ) -> int: """ @brief Reserve a set of GPIO lines. @param bulk: Set of GPIO lines to reserve. @param config: Request options. @param default_vals: Initial line values - only relevant if we're setting the direction to output. @return 0 if the all lines were properly requested. In case of an error this routine returns -1 and sets the last error number. If this routine succeeds, the caller takes ownership of the GPIO lines until they're released. All the requested lines must be prodivided by the same gpiochip. """ if not _line_bulk_same_chip(bulk) or not _line_bulk_all_free(bulk): return -1 if _line_request_is_direction(config.request_type): return _line_request_values(bulk, config, default_vals) if _line_request_is_events(config.request_type): return _line_request_events(bulk, config) set_errno(EINVAL) return -1
def check_connection(self): self.py_con._sys_recv(self.py_con._socket.fileno(), ' ', 1, socket.MSG_DONTWAIT | socket.MSG_PEEK) if ctypes.get_errno() == errno.EAGAIN: ctypes.set_errno(0) return True return False
def os_error(): """create OSError from C errno. And clear C errno""" en = ctypes.get_errno() ctypes.set_errno(0) if en == 0: return OSError(en, "(no errno found)") else: return OSError(en, errno.errorcode[en])
def check_connection(self): rc = self.py_con._sys_recv( self.py_con._socket.fileno(), ' ', 1, socket.MSG_DONTWAIT | socket.MSG_PEEK ) if ctypes.get_errno() == errno.EAGAIN: ctypes.set_errno(0) return True return False
def check(): # Check that connection is alive buf = ctypes.create_string_buffer(2) self._sys_recv(self._socket.fileno(), buf, 1, socket.MSG_DONTWAIT | socket.MSG_PEEK) if ctypes.get_errno() == errno.EAGAIN: ctypes.set_errno(0) return errno.EAGAIN return (ctypes.get_errno() if ctypes.get_errno() else errno.ECONNRESET)
def errcheck(ret, func, args): # make cross platform if ret != 0: e = ctypes.get_errno() ctypes.set_errno(0) if e in errors: #make this different exceptions raise OSError(errors[e]) return ret
def __call__(self, *args): ctypes.set_errno(0) try: return ctypes._CFuncPtr.__call__(self, *args) finally: errno = ctypes.get_errno() if errno: import os raise IOError(errno, os.strerror(errno))
def _line_request_direction_is_valid(direction: int) -> bool: if ( direction == GPIOD_LINE_REQUEST_DIRECTION_AS_IS or direction == GPIOD_LINE_REQUEST_DIRECTION_INPUT or direction == GPIOD_LINE_REQUEST_DIRECTION_OUTPUT ): return True set_errno(EINVAL) return False
def RemoveLinkTargetAndLink(link_name, fail_on_dangling=False): if not os.path.islink(link_name): Error("in util.RemoveLinkTargetAndLink: \"" + link_name + "\" is not a symlink!") try: link_target = os.readlink(link_name) ctypes.set_errno(0) os.unlink(link_target) except Exception as e: if not fail_on_dangling or ctypes.get_errno() != errno.ENOENT: Error("in util.RemoveLinkTargetAndLink: can't delete link target of \"" + link_name + "\"!") os.unlink(link_name)
def _line_bulk_same_chip(bulk: gpiod_line_bulk) -> bool: if bulk.num_lines == 1: return True first_chip = bulk[0].chip for it in bulk: if it.chip != first_chip: set_errno(EINVAL) return False return True
def __call__(self, attr, data): if data is not None: # sorry, I do not know how to # raise Exception("from your callback") ctypes.set_errno(data) return mnl.MNL_CB_ERROR preval = self.val self.val += 1 if preval == attr.get_u8(): return mnl.MNL_CB_OK else: return mnl.MNL_CB_STOP
def test_errno_saved_and_restored(self): def check(): assert _rawffi.get_errno() == 42 assert ctypes.get_errno() == old check.free_temp_buffers = lambda *args: None f = function.CFuncPtr() old = _rawffi.get_errno() f._flags_ = _rawffi.FUNCFLAG_USE_ERRNO ctypes.set_errno(42) f._call_funcptr(check) assert _rawffi.get_errno() == old ctypes.set_errno(0)
def gpiod_line_event_wait_bulk( bulk: gpiod_line_bulk, timeout: timedelta, event_bulk: Optional[gpiod_line_bulk], ) -> int: """ @brief Wait for events on a set of lines. @param bulk: Set of GPIO lines to monitor. @param timeout: Wait time limit. @param event_bulk: Bulk object in which to store the line handles on which events occurred. Can be None. @return 0 if wait timed out, -1 if an error occurred, 1 if at least one event occurred. """ if not _line_bulk_same_chip(bulk) or not _line_bulk_all_requested(bulk): return -1 poll = select.poll() fd_to_line = {} for it in bulk: poll.register(it.fd_handle.fd, POLLIN | POLLPRI) fd_to_line[it.fd_handle.fd] = it timeout_ms = ( (timeout.days * 86_400_000) + (timeout.seconds * 1_000) + (timeout.microseconds / 1000.0) ) revents = poll.poll(timeout_ms) if revents is None: return -1 if len(revents) == 0: return 0 for it in revents: fd = it[0] revent = it[1] if revent: if revent & POLLNVAL: set_errno(EINVAL) return -1 if event_bulk is not None: event_bulk.add(fd_to_line[fd]) return 1
def cb_err(nlh, d): err = nlh.get_payload_as(netlink.Nlmsgerr) if nlh.nlmsg_len < nlh.size(netlink.Nlmsgerr.csize()): set_errno(errno.EBADMSG) return mnl.MNL_CB_ERROR if err.error < 0: en = - err.error else: en = err.error if errno == 0: return mnl.MNL_CB_STOP else: set_errno(en) return mnl.MNL_CB_ERROR
def RemoveLinkTargetAndLink(link_name, fail_on_dangling=False): if not os.path.islink(link_name): Error("in util.RemoveLinkTargetAndLink: \"" + link_name + "\" is not a symlink!") try: link_target = os.readlink(link_name) ctypes.set_errno(0) os.unlink(link_target) except Exception as e: if not fail_on_dangling or ctypes.get_errno() != errno.ENOENT: Error( "in util.RemoveLinkTargetAndLink: can't delete link target of \"" + link_name + "\"!") os.unlink(link_name)
def check(): # Check that connection is alive buf = ctypes.create_string_buffer(2) try: sock_fd = self._socket.fileno() except socket.error as e: if e.errno == errno.EBADF: return errno.ECONNRESET else: self._sys_recv(sock_fd, buf, 1, socket.MSG_DONTWAIT | socket.MSG_PEEK) if ctypes.get_errno() == errno.EAGAIN: ctypes.set_errno(0) return errno.EAGAIN return (ctypes.get_errno() if ctypes.get_errno() else errno.ECONNRESET)
def errcheck(result, func, arguments): if result == -1: errno = ctypes.set_errno(0) raise IOError(errno, 'tee: %s' % os.strerror(errno)) else: return result
def getcanonicalpath(name): fd = os.open(name, os.O_RDONLY, 0) try: numchars = 1024 # MAXPATHLEN # The kernel caps this routine to MAXPATHLEN, so there is no # point in over-allocating or trying again with a larger buffer buf = ctypes.create_string_buffer(numchars) ctypes.set_errno(0) result = getpathfcntl(fd, F_GETPATH, buf) if result != 0: raise OSError(ctypes.get_errno()) # buf is a bytes buffer, so normalize it if necessary ret = buf.value if isinstance(name, compat.UNICODE): ret = os.fsdecode(ret) return ret finally: os.close(fd)
def get_canonical_filesystem_path(name): fd = os.open(name, os.O_RDONLY, 0) try: numchars = 1024 # MAXPATHLEN # The kernel caps this routine to MAXPATHLEN, so there is no # point in over-allocating or trying again with a larger buffer buf = ctypes.create_string_buffer(numchars) ctypes.set_errno(0) result = getpath_fcntl(fd, F_GETPATH, buf) if result != 0: raise OSError(ctypes.get_errno()) # buf is a bytes buffer, so normalize it if necessary ret = buf.value if isinstance(name, compat.UNICODE): ret = os.fsdecode(ret) return ret finally: os.close(fd)
def gpiod_line_event_get_fd(line: gpiod_line) -> int: """ @brief Get the event file descriptor. @param line: GPIO line object. @return Number of the event file descriptor or -1 if the user tries to retrieve the descriptor from a line that wasn't configured for event monitoring. Users may want to poll the event file descriptor on their own. This routine allows to access it. """ if line.state != _LINE_REQUESTED_EVENTS: set_errno(EPERM) return -1 return line.fd_handle.fd
def _sem_timedwait(handle, timeout): t_start = time.time() if sys.platform != "darwin": sec = int(timeout) tv_sec = int(t_start) nsec = int(1e9 * (timeout - sec) + .5) tv_nsec = int(1e9 * (t_start - tv_sec) + .5) deadline = timespec(sec + tv_sec, nsec + tv_nsec) deadline.tv_sec += int(deadline.tv_nsec / 1000000000) deadline.tv_nsec %= 1000000000 return pthread.sem_timedwait(handle, ctypes.pointer(deadline)) # PERFORMANCE WARNING # No sem_timedwait on OSX so we implement our own method. This method can # degrade performances has the wait can have a latency up to 20 msecs deadline = t_start + timeout delay = 0 now = time.time() while True: # Poll the sem file res = pthread.sem_trywait(handle) if res == 0: return 0 else: e = ctypes.get_errno() if e != errno.EAGAIN: raiseFromErrno() # check for timeout now = time.time() if now > deadline: ctypes.set_errno(errno.ETIMEDOUT) return -1 # calculate how much time left and check the delay is not too long # -- maximum is 20 msecs difference = (deadline - now) delay = min(delay, 20e-3, difference) # Sleep and increase delay time.sleep(delay) delay += 1e-3
def _sem_timedwait(handle, timeout): t_start = time.time() if sys.platform != "darwin": sec = int(timeout) tv_sec = int(t_start) nsec = int(1e9 * (timeout - sec) + .5) tv_nsec = int(1e9 * (t_start - tv_sec) + .5) deadline = timespec(sec+tv_sec, nsec+tv_nsec) deadline.tv_sec += int(deadline.tv_nsec / 1000000000) deadline.tv_nsec %= 1000000000 return pthread.sem_timedwait(handle, ctypes.pointer(deadline)) # PERFORMANCE WARNING # No sem_timedwait on OSX so we implement our own method. This method can # degrade performances has the wait can have a latency up to 20 msecs deadline = t_start + timeout delay = 0 now = time.time() while True: # Poll the sem file res = pthread.sem_trywait(handle) if res == 0: return 0 else: e = ctypes.get_errno() if e != errno.EAGAIN: raiseFromErrno() # check for timeout now = time.time() if now > deadline: ctypes.set_errno(errno.ETIMEDOUT) return -1 # calculate how much time left and check the delay is not too long # -- maximum is 20 msecs difference = (deadline - now) delay = min(delay, 20e-3, difference) # Sleep and increase delay time.sleep(delay) delay += 1e-3
def peekuser(pid, offset): """ Reads a word from offset C{offset} in the child's user area, which holds registers and other information about the process. @param pid: The child process id. @type pid: pid_t @param offset: The byte offset in the user area to read. @type offset: int """ set_errno(0) result = ptrace(PTRACE_PEEKUSER, pid, cast(offset, c_void_p), None) if result == -1: e = get_errno() if e != 0: raise_from_errno(e) return c_long(result).value
def mklog(testname: str, flags: int = 0) -> typing.BinaryIO: """ Create and open a log file for a test, as implemented by ptef_mklog(3). Instead of returning an open file descriptor number, like the C function would, this interface transforms it into a python File-like object, so you can do ie. with ptef.mklog(test) as f: f.write(...) # or subprocess.run(['...'], stderr=f) """ ctypes.set_errno(0) fd = libptef.ptef_mklog(ctypes.c_char_p(testname.encode('utf-8')), ctypes.c_int(flags)) if fd == -1: errno = ctypes.get_errno() raise OSError(errno, os.strerror(errno), None) else: return os.fdopen(fd, 'wb')
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)
def peekdata(pid, address): """ Reads a word at location C{address} in the child's data area memory, returning the word as the result. @param pid: The child process id. @type pid: pid_t @param address: The address in the child process' memory space to retrieve. @type address: c_void_p """ set_errno(0) result = ptrace(PTRACE_PEEKDATA, pid, cast(address, c_void_p), None) if result == -1: e = get_errno() if e != 0: raise_from_errno(e) return c_long(result).value
def gpiod_line_event_read_fd(fd: int, event: gpiod_line_event) -> int: """ @brief Read the last GPIO event directly from a file descriptor. @param fd: File descriptor. @param event: Buffer in which the event data will be stored. @return 0 if the event was read correctly, -1 on error. Users who directly poll the file descriptor for incoming events can also directly read the event data from it using this routine. This function translates the kernel representation of the event to the libgpiod format. """ evdata = gpioevent_data() try: rd = os_read(fd, sizeof(evdata)) except OSError: return -1 if len(rd) != sizeof(evdata): set_errno(EIO) return -1 memmove(pointer(evdata), rd, sizeof(evdata)) event.event_type = ( GPIOD_LINE_EVENT_RISING_EDGE if evdata.id == GPIOEVENT_EVENT_RISING_EDGE else GPIOD_LINE_EVENT_FALLING_EDGE ) sec = evdata.timestamp // 1_000_000_000 event.ts = datetime(year=1970, month=1, day=1) + timedelta( days=sec // 86400, seconds=sec % 86400, microseconds=(evdata.timestamp % 1_000_000_000) // 1000, ) return 0
def _call_ptrace(self, fct, *args): '''Helper method allowing to check if ptrace returned an error. Parameters ---------- fct : fct One the ptrace helpers defined into the ptrace module (e.g attach). *args Arguments given to the ptrace helper. Do not provide the pid, this one is automatically given by this method. Raises ------ PtraceException If a ptrace call failed. ''' ctypes.set_errno(0) res = fct(self._pid, *args) errno = ctypes.get_errno() if errno != 0: raise PtraceException(f'ptrace failed, errno: {errno}') return res
def get_address_family(fd): log.msg('Resolving address family of FD %d' % fd) fd_ = ctypes.c_int(fd) addr = ctypes.c_ushort(0) len_ = ctypes.c_int(ctypes.sizeof(addr)) ctypes.set_errno(0) res = getsockname(fd_, ctypes.byref(addr), ctypes.byref(len_)) if res != 0: e = ctypes.get_errno() raise OSError(e, os.strerror(e)) af = addr.value if af in af_map: log.msg('Found address family of FD %d: %s' % (fd, af_map[af])) else: log.msg('Unknown address family of FD %d: %d' % (fd, af)) return af
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)
def ptrace(command, pid=0, arg1=0, arg2=0, check_errno=False): if HAS_CPTRACE: try: set_errno(0) result = _ptrace(command, pid, arg1, arg2, check_errno) except ValueError as errobj: message = str(errobj) errno = get_errno() raise PtraceError(message, errno=errno, pid=pid) else: result = _ptrace(command, pid, arg1, arg2) result_signed = c_long(result).value if result_signed == -1: errno = get_errno() # peek operations may returns -1 with errno=0: # it's not an error. For other operations, -1 # is always an error if not (check_errno) or errno: message = "ptrace(cmd=%s, pid=%s, %r, %r) error #%s: %s" % ( command, pid, arg1, arg2, errno, strerror(errno)) raise PtraceError(message, errno=errno, pid=pid) return result
def ptrace(command, pid=0, arg1=0, arg2=0, check_errno=False): if HAS_CPTRACE: try: set_errno(0) result = _ptrace(command, pid, arg1, arg2, check_errno) except ValueError as errobj: message = str(errobj) errno = get_errno() raise PtraceError(message, errno=errno, pid=pid) else: result = _ptrace(command, pid, arg1, arg2) result_signed = c_long(result).value if result_signed == -1: errno = get_errno() # peek operations may returns -1 with errno=0: # it's not an error. For other operations, -1 # is always an error if not(check_errno) or errno: message = "ptrace(cmd=%s, pid=%s, %r, %r) error #%s: %s" % ( command, pid, arg1, arg2, errno, strerror(errno)) raise PtraceError(message, errno=errno, pid=pid) return result
def gpiod_chip_find_line(chip: gpiod_chip, name: str) -> Optional[gpiod_line]: """ @brief Find a GPIO line by name among lines associated with given GPIO chip. @param chip: The GPIO chip object. @param name: The name of the GPIO line. @return The GPIO line handle or None if the line could not be found or an error occurred. @note In case a line with given name is not associated with given chip, the function sets errno to ENOENT. """ line_iter = gpiod_line_iter(chip) if line_iter is None: return None for line in line_iter: if line.name and line.name == name: return line set_errno(ENOENT) return None
def _is_gpiochip_cdev(path: str) -> bool: try: statbuf = lstat(path) except FileNotFoundError: return False # Is it a character device? if not S_ISCHR(statbuf.st_mode): # Passing a file descriptor not associated with a character # device to ioctl() makes it set errno to ENOTTY. Let's do # the same in order to stay compatible with the versions of # libgpiod from before the introduction of this routine. set_errno(ENOTTY) return False # Do we have a corresponding sysfs attribute? name = basename(path) sysfsp = "/sys/bus/gpio/devices/{}/dev".format(name) if not access(sysfsp, R_OK): # This is a character device but not the one we're after. # Before the introduction of this function, we'd fail with # ENOTTY on the first GPIO ioctl() call for this file # descriptor. Let's stay compatible here and keep returning # the same error code. set_errno(ENOTTY) return False # Make sure the major and minor numbers of the character device # correspond to the ones in the dev attribute in sysfs. devstr = "{}:{}".format(major(statbuf.st_rdev), minor(statbuf.st_rdev)) try: with open(sysfsp, "r") as fd: sysfsdev = fd.read(len(devstr)) except FileNotFoundError: return False if sysfsdev != devstr: set_errno(ENODEV) return False return True
def set_errno(self, value): ctypes.set_errno(value)
def recv(self, data): if platform.system().lower() == "windows": WSASetLastError(errno.WSAEWOULDBLOCK) ctypes.set_errno(errno.EAGAIN) return -1
def _tls_custom_io_recv(self, transport_ptr, data, data_len): if platform.system().lower() == "windows": WSASetLastError(errno.WSAEWOULDBLOCK) ctypes.set_errno(errno.EAGAIN) return -1
def _ignore_closed(self, result): if ctypes.get_errno() == WEPoll.EBADF: # /* fd already closed */ ctypes.set_errno(0) result = 0 return result
def cb_overrun(nlh, d): set_errno(errno.ENOSPC) return mnl.MNL_CB_ERROR