Skip to content

Commit

Permalink
plan9: basic server with read/write support
Browse files Browse the repository at this point in the history
  • Loading branch information
svinota committed Jun 3, 2024
1 parent ad0d6e2 commit ac69658
Show file tree
Hide file tree
Showing 5 changed files with 405 additions and 168 deletions.
64 changes: 8 additions & 56 deletions pyroute2/netlink/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -843,6 +843,8 @@ class nlmsg_base(dict):
prefix = None
own_parent = False
header_type = None
header_fmt = None
decode_as = None
# caches
__compiled_nla = False
__compiled_ft = False
Expand Down Expand Up @@ -1639,7 +1641,7 @@ def decode_nlas(self, offset):
class nlmsg_decoder_generic(object):

@staticmethod
def ft_decode_field(fmt, data, offset):
def decode_field(fmt, data, offset):
global cache_fmt
##
# ~~ size = struct.calcsize(efmt)
Expand All @@ -1660,41 +1662,12 @@ def ft_decode_field(fmt, data, offset):
else:
return value, offset

@staticmethod
def ft_decode_struct(fmt, data, offset):
if hasattr(fmt, 'header_fmt'):
# variable length data
header = fmt.header_fmt
header_length = struct.calcsize(header)
(length,) = struct.unpack_from(header, data, offset)
offset += header_length
if fmt.base is str:
(value,) = struct.unpack_from(f'{length}s', data, offset)
value = value.decode('utf-8')
else:
value = fmt.base(data[offset : offset + length])
offset += length
elif hasattr(fmt, 'decode_count'):
# array
value, offset = fmt.decode(data, offset)
elif hasattr(fmt, 'length'):
# fixed length struct
value = fmt.decode(data[offset : offset + fmt.length], 0)
offset += fmt.length
else:
raise ValueError('can not use decoder type')
return value, offset

def ft_decode(self, offset):
for name, fmt in self.fields:
if isinstance(fmt, str):
self[name], offset = self.ft_decode_field(
fmt, self.data, offset
)
self[name], offset = self.decode_field(fmt, self.data, offset)
elif isinstance(fmt, type):
self[name], offset = self.ft_decode_struct(
fmt, self.data, offset
)
self[name], offset = fmt.decode_from(self.data, offset)
# read NLA chain
if self.nla_map:
offset = (offset + 4 - 1) & ~(4 - 1)
Expand Down Expand Up @@ -1747,26 +1720,9 @@ def ft_decode(self, offset):


class nlmsg_encoder_generic(object):
@staticmethod
def ft_encode_struct(fmt, data, offset, value, zstring=0):
if hasattr(fmt, 'header_fmt'):
# variable length
length = len(value)
total_length = length + struct.calcsize(fmt.header_fmt)
data.extend([0] * total_length)
struct.pack_into(
f'{fmt.header_fmt}{length}s',
data,
offset,
length,
value.encode('utf-8'),
)
return offset + total_length
else:
return fmt.encode(data, offset, value)

@staticmethod
def ft_encode_field(fmt, data, offset, value, zstring=0):
def encode_field(fmt, data, offset, value, zstring=0):
if fmt == 's':
length = len(value or '') + zstring
efmt = '%is' % (length)
Expand Down Expand Up @@ -1806,13 +1762,9 @@ def ft_encode(self, offset):
value = self[name]

if isinstance(fmt, str):
offset = self.ft_encode_field(
fmt, self.data, offset, value, zs
)
offset = self.encode_field(fmt, self.data, offset, value, zs)
elif isinstance(fmt, type):
offset = self.ft_encode_struct(
fmt, self.data, offset, value, zs
)
offset = fmt.encode_into(self.data, offset, value)

diff = 0
if self.align > 0:
Expand Down
7 changes: 4 additions & 3 deletions pyroute2/netlink/nlsocket.py
Original file line number Diff line number Diff line change
Expand Up @@ -382,8 +382,6 @@ def __init__(
}
)
self.status = self.spec.status
print(self.spec)
print(self.status)
self.capabilities = {
'create_bridge': config.kernel > [3, 2, 0],
'create_bond': config.kernel > [3, 2, 0],
Expand Down Expand Up @@ -603,7 +601,10 @@ def get(
# step 1. receive as much as we can from the socket
while True:
try:
self.buffer.append(self.getdata(block=False))
data = self.getdata(block=False)
if len(data) == 0 or data[0] == 0:
return
self.buffer.append(data)
except BlockingIOError:
break
if len(self.buffer) == 0:
Expand Down
191 changes: 156 additions & 35 deletions pyroute2/plan9/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import struct

from pyroute2.netlink import (
nlmsg,
nlmsg_decoder_generic,
nlmsg_encoder_generic,
)
from pyroute2.netlink import nlmsg
from pyroute2.netlink.nlsocket import Marshal

Tversion = 100
Expand Down Expand Up @@ -48,35 +44,36 @@ def decode_count(data, offset):
return count, offset + struct.calcsize(header)

@staticmethod
def decode(data, offset):
def decode_from(data, offset):
count, offset = CustomArray.decode_count(data, offset)
ret = []
for _ in range(count):
value, offset = nlmsg_decoder_generic.ft_decode_struct(
kind, data, offset
)
value, offset = kind.decode_from(data, offset)
ret.append(value)
return ret, offset

@staticmethod
def encode(data, offset, value):
def encode_into(data, offset, value):
data.extend([0] * struct.calcsize(header))
struct.pack_into(header, data, offset, len(value))
offset += struct.calcsize(header)
for item in value:
offset = nlmsg_encoder_generic.ft_encode_struct(
kind, data, offset, item
)
offset = kind.encode_into(data, offset, item)
return offset

return CustomArray


class Qid:
class Qid(dict):
length = 13

def __init__(self, qtype, vers, path):
self['type'] = qtype
self['vers'] = vers
self['path'] = path

@staticmethod
def decode(data, offset):
def decode_from(data, offset):
return dict(
zip(
('type', 'vers', 'path'),
Expand All @@ -85,22 +82,149 @@ def decode(data, offset):
)

@staticmethod
def encode(data, offset, value):
def encode_into(data, offset, value):
data.extend([0] * Qid.length)
struct.pack_into(
'=BIQ', data, offset, value['type'], value['vers'], value['path']
)
return offset + Qid.length


class Stat(dict):
header_fmt = 'H'

def __init__(self):
self['size'] = 58
self['type'] = 0
self['dev'] = 0
self['qid.type'] = 0
self['qid.vers'] = 0
self['qid.path'] = 0
self['mode'] = 0
self['atime'] = 0
self['mtime'] = 0
self['length'] = 0
self['name'] = ''
self['uid'] = ''
self['gid'] = ''
self['muid'] = ''

@staticmethod
def decode_from(data, offset=0):
ret = dict(
zip(
(
'size',
'type',
'dev',
'qid.type',
'qid.vers',
'qid.path',
'mode',
'atime',
'mtime',
'length',
),
struct.unpack_from('=HHIBIQIIIQ', data, offset),
)
)
offset += 42
for key in ('name', 'uid', 'gid', 'muid'):
ret[key], offset = String.decode_from(data, offset)
return ret

@staticmethod
def encode_into(data, offset, value):
data.extend([0] * 41)
# size of all the data except uint16 `size` header
# 41 + 2 + name + 2 + uid + 2 + gid + 2 + muid
value['size'] = (
47
+ len(value['name'])
+ len(value['uid'])
+ len(value['gid'])
+ len(value['muid'])
)
struct.pack_into(
'=HHIBIQIIIQ',
data,
offset,
value['size'],
value['type'],
value['dev'],
value['qid.type'],
value['qid.vers'],
value['qid.path'],
value['mode'],
value['atime'],
value['mtime'],
value['length'],
)
offset += 41
for key in ('name', 'uid', 'gid', 'muid'):
offset = String.encode_into(data, offset, value[key])
return offset


class WStat(Stat):
@staticmethod
def decode_from(data, offset=0):
# just ignore plength for now
return Stat.decode_from(data, offset + 2)

@staticmethod
def encode_into(data, offset, value):
data.extend([0] * 2)
new_offset = Stat.encode_into(data, offset + 2, value)
# size of all the data except uint16 `plength` header
struct.pack_into('H', data, offset, new_offset - offset - 2)
return new_offset


class CData:
header_fmt = 'I'
base = bytearray

@staticmethod
def decode_from(data, offset=0):
(length,) = struct.unpack_from(CData.header_fmt, data, offset)
offset += struct.calcsize(CData.header_fmt)
return bytearray(data[offset : offset + length]), offset + length

@staticmethod
def encode_into(data, offset, value):
length = len(value)
if isinstance(value, str):
value = value.encode('utf-8')
data.extend([0] * (length + struct.calcsize(CData.header_fmt)))
struct.pack_into(
f'{CData.header_fmt}{length}s', data, offset, length, value
)
return offset + length + struct.calcsize(CData.header_fmt)


class String:
header_fmt = 'H'
base = str

@staticmethod
def decode_from(data, offset=0):
(length,) = struct.unpack_from(String.header_fmt, data, offset)
offset += struct.calcsize(String.header_fmt)
(value,) = struct.unpack_from(f'{length}s', data, offset)
value = value.decode('utf-8')
return value, offset + length

@staticmethod
def encode_into(data, offset, value):
length = len(value)
data.extend([0] * (length + struct.calcsize(String.header_fmt)))
struct.pack_into(
f'{String.header_fmt}{length}s',
data,
offset,
length,
value.encode('utf-8'),
)
return offset + length + struct.calcsize(String.header_fmt)


class msg_base(nlmsg):
Expand All @@ -111,6 +235,7 @@ class msg_base(nlmsg):
class msg_terror(msg_base):
defaults = {'header': {'type': Terror}}


class msg_rerror(msg_base):
defaults = {'header': {'type': Rerror}}
fields = (('ename', String),)
Expand Down Expand Up @@ -168,23 +293,7 @@ class msg_tstat(msg_base):

class msg_rstat(msg_base):
defaults = {'header': {'type': Rstat}}
fields = (
('plength', 'H'),
('size', 'H'),
('type', 'H'),
('dev', 'I'),
('qid.type', 'B'),
('qid.vers', 'I'),
('qid.path', 'Q'),
('mode', 'I'),
('atime', 'I'),
('mtime', 'I'),
('length', 'Q'),
('name', String),
('uid', String),
('gid', String),
('muid', String),
)
fields = (('stat', WStat),)


class msg_tclunk(msg_base):
Expand Down Expand Up @@ -217,6 +326,16 @@ class msg_rread(msg_base):
fields = (('data', CData),)


class msg_twrite(msg_base):
defaults = {'header': {'type': Twrite}}
fields = (('fid', 'I'), ('offset', 'Q'), ('data', CData))


class msg_rwrite(msg_base):
defaults = {'header': {'type': Rwrite}}
fields = (('count', 'I'),)


class Marshal9P(Marshal):
default_message_class = msg_rerror
error_type = Rerror
Expand All @@ -238,6 +357,8 @@ class Marshal9P(Marshal):
Rclunk: msg_rclunk,
Tstat: msg_tstat,
Rstat: msg_rstat,
Twrite: msg_twrite,
Rwrite: msg_rwrite,
}

def parse(self, data, seq=None, callback=None, skip_alien_seq=False):
Expand Down
Loading

0 comments on commit ac69658

Please sign in to comment.