Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: CI

on:
push:
branches:
- main
pull_request:

jobs:
build:
name: Build distribution
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: actions/setup-python@v6
with:
python-version: "3.12"
- run: python -m pip install build twine
- run: python -m build
- run: python -m twine check dist/*
- run: python -m pip install dist/*.whl
- run: python -c "import freebsd_sysctl; print(freebsd_sysctl.__version__)"

test-freebsd:
name: Test on FreeBSD ${{ matrix.release }}
runs-on: ubuntu-latest
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
release: ["14.4", "15.1"]
steps:
- uses: actions/checkout@v7
- name: Enable KVM
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' \
| sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
- name: Install QEMU
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
qemu-system-x86 qemu-utils ovmf genisoimage
- name: Cache FreeBSD VM image
uses: actions/cache@v6
with:
path: ~/.cache/freebsd_sysctl-vm/*.xz
key: freebsd-vm-${{ matrix.release }}-v1
- name: Run the test suite in a FreeBSD VM
run: tests/vm/run.sh --release "${{ matrix.release }}"
- name: Upload VM console log
if: failure()
uses: actions/upload-artifact@v7
with:
name: vm-console-${{ matrix.release }}
path: /tmp/freebsd_sysctl-vm-*/console.log
if-no-files-found: ignore
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ build/
dist/
*.egg-info/
.eggs/
*.qcow2
*.iso
214 changes: 55 additions & 159 deletions freebsd_sysctl/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
# POSSIBILITY OF SUCH DAMAGE.
import typing
import ctypes
import errno
import struct
import enum
import freebsd_sysctl.libc
Expand All @@ -31,9 +32,7 @@

from freebsd_sysctl.__version__ import __version__

NULL_BYTES = b"\x00"
CTL_MAXNAME = ctypes.c_uint(24)
T_OID = (ctypes.c_int * 2)
CTL_MAXNAME = 24
BUFSIZ = 1024 # see /include/stdio.h#L209


Expand Down Expand Up @@ -117,206 +116,103 @@ def next(self):
def children(self) -> typing.Iterator['Sysctl']:
if self.ctl_type != freebsd_sysctl.types.NODE:
return
current = self.next
while self.oid == current.oid[:len(self.oid)]:
yield current
current = current.next
try:
current = self.next
while self.oid == current.oid[:len(self.oid)]:
yield current
current = current.next
except OSError as err:
# the last OID of the sysctl tree has no successor
if err.errno != errno.ENOENT:
raise

def __query_kind_and_fmt(self) -> None:
self._kind, self._fmt = self.query_fmt(self.oid)

@staticmethod
def name2oid(name: str) -> typing.List[int]:
p_name = ctypes.c_char_p(name.encode() + NULL_BYTES)
oid = T_OID(0, 3)
p_oid = ctypes.POINTER(T_OID)(oid)

length = ctypes.c_int(CTL_MAXNAME.value * ctypes.sizeof(ctypes.c_int))
p_length = ctypes.POINTER(ctypes.c_int)(length)

Res = ctypes.c_int*length.value
res = (Res)()

freebsd_sysctl.libc.dll.sysctl(
p_oid,
2,
ctypes.POINTER(Res)(res),
p_length,
p_name,
len(p_name.value)
encoded_name = name.encode()
res = (ctypes.c_int * CTL_MAXNAME)()
oldlen = ctypes.c_size_t(ctypes.sizeof(res))

freebsd_sysctl.libc.sysctl(
[0, 3],
res,
oldlen,
ctypes.c_char_p(encoded_name),
len(encoded_name)
)

oid_length = int(length.value / ctypes.sizeof(ctypes.c_int))
oid_length = int(oldlen.value / ctypes.sizeof(ctypes.c_int))
return res[:oid_length]

@staticmethod
def oid2name(oid: typing.List[int]) -> str:
qoid_len = (2 + len(oid))
qoid_type = ctypes.c_int * qoid_len
qoid = (qoid_type)(*([0, 1] + oid))
p_qoid = ctypes.POINTER(qoid_type)(qoid)

buf = ctypes.create_string_buffer(BUFSIZ)
buf_void = ctypes.cast(buf, ctypes.c_void_p)

buf_length = ctypes.sizeof(buf)
p_buf_length = ctypes.POINTER(ctypes.c_int)(ctypes.c_int(buf_length))

freebsd_sysctl.libc.dll.sysctl(
p_qoid,
qoid_len,
buf_void,
p_buf_length,
0,
0
)
oldlen = ctypes.c_size_t(ctypes.sizeof(buf))
freebsd_sysctl.libc.sysctl([0, 1] + oid, buf, oldlen)
return buf.value.decode()

@staticmethod
def query_fmt(oid: typing.List[int]) -> typing.Tuple[int, str]:
buf = ctypes.create_string_buffer(BUFSIZ)
oldlen = ctypes.c_size_t(ctypes.sizeof(buf))
freebsd_sysctl.libc.sysctl([0, 4] + oid, buf, oldlen)

qoid_len = (2 + len(oid))
qoid_type = ctypes.c_int * qoid_len
qoid = (qoid_type)(*([0, 4] + oid))
p_qoid = ctypes.POINTER(qoid_type)(qoid)

buf_type = ctypes.c_char * BUFSIZ
buf = buf_type()
p_buf = ctypes.POINTER(buf_type)(buf)
buf_void = ctypes.cast(p_buf, ctypes.c_void_p)

buf_length = ctypes.sizeof(buf)
p_buf_length = ctypes.POINTER(ctypes.c_int)(ctypes.c_int(buf_length))

freebsd_sysctl.libc.dll.sysctl(
p_qoid,
qoid_len,
buf_void,
p_buf_length,
0,
0
)

if len(buf) < 4:
if oldlen.value < 4:
raise Exception("response buffer too small")
result = buf[:buf_length]
result = buf.raw[:oldlen.value]
kind, = struct.unpack("I", result[:4])
null_pos = result.find(b'\x00',4) # buf is large and string is small
null_pos = result.find(b"\x00", 4)
fmt = result[4:null_pos].decode()
return (kind, fmt)

@staticmethod
def query_size(
oid: typing.List[int],
ctl_type: freebsd_sysctl.types.CtlType
) -> bytes:

oid_type = ctypes.c_int * len(oid)
_oid = (oid_type)(*oid)
p_oid = ctypes.POINTER(oid_type)(_oid)

length = ctypes.c_int()
p_length = ctypes.POINTER(ctypes.c_int)(length)

freebsd_sysctl.libc.dll.sysctl(
p_oid,
len(oid),
None,
p_length,
0
)

return max(length.value, ctl_type.min_size)
) -> int:
oldlen = ctypes.c_size_t()
freebsd_sysctl.libc.sysctl(oid, None, oldlen)
return max(oldlen.value, ctl_type.min_size)

@staticmethod
def query_value(
oid: typing.List[int],
size: int,
ctl_type: freebsd_sysctl.types.CtlType
) -> bytes:

# ToDo: check if value is readable

oid_type = ctypes.c_int * len(oid)
_oid = (oid_type)(*oid)
p_oid = ctypes.POINTER(oid_type)(_oid)

buf_type = ctypes.c_char * size
buf = buf_type()
p_buf = ctypes.POINTER(buf_type)(buf)
p_buf_void = ctypes.cast(p_buf, ctypes.c_void_p)

buf_length = ctypes.sizeof(buf)
p_buf_length = ctypes.POINTER(ctypes.c_int)(ctypes.c_int(buf_length))

freebsd_sysctl.libc.dll.sysctl(
p_oid,
ctypes.c_uint32(len(oid)),
p_buf_void,
p_buf_length,
None,
0
)

return ctl_type(buf, size)
# the value can grow between the size query and the read
attempts = 4
while True:
buf = ctypes.create_string_buffer(size)
oldlen = ctypes.c_size_t(size)
try:
freebsd_sysctl.libc.sysctl(oid, buf, oldlen)
except OSError as err:
attempts -= 1
if (err.errno != errno.ENOMEM) or (attempts == 0):
raise
size *= 2
continue
return ctl_type(buf, size)

@staticmethod
def query_description(
oid: typing.List[int]
) -> str:

qoid_len = (2 + len(oid))
qoid_type = ctypes.c_int * qoid_len
qoid = (qoid_type)(*([0, 5] + oid))
p_qoid = ctypes.POINTER(qoid_type)(qoid)

buf_type = ctypes.c_char * BUFSIZ
buf = buf_type()
p_buf = ctypes.POINTER(buf_type)(buf)
buf_void = ctypes.cast(p_buf, ctypes.c_void_p)

buf_length = ctypes.sizeof(buf)
p_buf_length = ctypes.POINTER(ctypes.c_int)(ctypes.c_int(buf_length))

freebsd_sysctl.libc.dll.sysctl(
p_qoid,
qoid_len,
buf_void,
p_buf_length,
0,
0
)

buf = ctypes.create_string_buffer(BUFSIZ)
oldlen = ctypes.c_size_t(ctypes.sizeof(buf))
freebsd_sysctl.libc.sysctl([0, 5] + oid, buf, oldlen)
return buf.value.decode()

@staticmethod
def query_next(oid: typing.List[int]) -> bytes:
buf = (ctypes.c_int * CTL_MAXNAME)()
oldlen = ctypes.c_size_t(ctypes.sizeof(buf))
freebsd_sysctl.libc.sysctl([0, 2] + oid, buf, oldlen)

qoid_len = (2 + len(oid))
qoid_type = ctypes.c_int * qoid_len
qoid = (qoid_type)(*([0, 2] + oid))
p_qoid = ctypes.POINTER(qoid_type)(qoid)

buf_type = ctypes.c_int * CTL_MAXNAME.value
buf = buf_type()
p_buf = ctypes.POINTER(buf_type)(buf)
buf_void = ctypes.cast(p_buf, ctypes.c_void_p)

buf_length = ctypes.sizeof(buf)
p_buf_length = ctypes.POINTER(ctypes.c_int)(ctypes.c_int(buf_length))

freebsd_sysctl.libc.dll.sysctl(
p_qoid,
qoid_len,
buf_void,
p_buf_length,
0,
0
)

oid_length = int(
p_buf_length.contents.value / ctypes.sizeof(ctypes.c_int)
)
oid_length = int(oldlen.value / ctypes.sizeof(ctypes.c_int))
return buf[0:oid_length]

@property
Expand Down
Loading