Initial revision

This commit is contained in:
Guido van Rossum 1992-12-14 23:25:04 +00:00
parent 5f59d6018e
commit e3cafbe7b8
5 changed files with 909 additions and 0 deletions

19
Demo/rpc/T.py Normal file
View File

@ -0,0 +1,19 @@
import sys, os, time
def TSTART():
global t0, t1
u, s, cu, cs = os.times()
t0 = u+cu, s+cs, time.millitimer()
def TSTOP(*label):
global t0, t1
u, s, cu, cs = os.times()
t1 = u+cu, s+cs, time.millitimer()
tt = []
for i in range(3):
tt.append(t1[i] - t0[i])
[u, s, r] = tt
msg = ''
for x in label: msg = msg + (x + ' ')
msg = msg + `u` + ' user, ' + `s` + ' sys, ' + `r*0.001` + ' real\n'
sys.stderr.write(msg)

166
Demo/rpc/mountclient.py Normal file
View File

@ -0,0 +1,166 @@
# Mount RPC client -- RFC 1094 (NFS), Appendix A
# This module demonstrates how to write your own RPC client in Python.
# Since there is no RPC compiler for Python (yet), you must first
# create classes derived from Packer and Unpacker to handle the data
# types for the server you want to interface to. You then write the
# client class. If you want to support both the TCP and the UDP
# version of a protocol, use multiple inheritance as shown below.
from rpc import Packer, Unpacker, TCPClient, UDPClient
MOUNTPROG = 100005
MOUNTVERS = 1
FHSIZE = 32
# Packer derived class for Mount protocol clients.
# The only thing we need to pack beyond basic types is an 'fhandle'
class MountPacker(Packer):
def pack_fhandle(self, fhandle):
self.pack_fopaque(FHSIZE, fhandle)
# Unpacker derived class for Mount protocol clients.
# The important types we need to unpack are fhandle, fhstatus,
# mountlist and exportlist; mountstruct, exportstruct and groups are
# used to unpack components of mountlist and exportlist and the
# corresponding functions are passed as function argument to the
# generic unpack_list function.
class MountUnpacker(Unpacker):
def unpack_fhandle(self):
return self.unpack_fopaque(FHSIZE)
def unpack_fhstatus(self):
status = self.unpack_uint()
if status == 0:
fh = self.unpack_fhandle()
else:
fh = None
return status, fh
def unpack_mountlist(self):
return self.unpack_list(self.unpack_mountstruct)
def unpack_mountstruct(self):
hostname = self.unpack_string()
directory = self.unpack_string()
return (hostname, directory)
def unpack_exportlist(self):
return self.unpack_list(self.unpack_exportstruct)
def unpack_exportstruct(self):
filesys = self.unpack_string()
groups = self.unpack_groups()
return (filesys, groups)
def unpack_groups(self):
return self.unpack_list(self.unpack_string)
# These are the procedures specific to the Mount client class.
# Think of this as a derived class of either TCPClient or UDPClient.
class PartialMountClient:
# This method is called by Client.init to initialize
# self.packer and self.unpacker
def addpackers(self):
self.packer = MountPacker().init()
self.unpacker = MountUnpacker().init('')
# The methods Mnt, Dump etc. each implement one Remote
# Procedure Call. Their general structure is
# self.start_call(<procedure-number>)
# <pack arguments using self.packer>
# self.do_call() # This does the actual message exchange
# <unpack reply using self.unpacker>
# self.end_call()
# return <reply>
# If the call fails, an exception is raised by do_call().
# If the reply does not match what you unpack, an exception is
# raised either during unpacking (if you overrun the buffer)
# or by end_call() (if you leave values in the buffer).
# Calling packer methods with invalid arguments (e.g. if
# invalid arguments were passed from outside) will also result
# in exceptions during packing.
def Mnt(self, directory):
self.start_call(1)
self.packer.pack_string(directory)
self.do_call()
stat = self.unpacker.unpack_fhstatus()
self.end_call()
return stat
def Dump(self):
self.start_call(2)
self.do_call()
list = self.unpacker.unpack_mountlist()
self.end_call()
return list
def Umnt(self, directory):
self.start_call(3)
self.packer.pack_string(directory)
self.do_call()
self.end_call()
def Umntall(self):
self.start_call(4)
self.do_call()
self.end_call()
def Export(self):
self.start_call(5)
self.do_call()
list = self.unpacker.unpack_exportlist()
self.end_call()
return list
# We turn the partial Mount client into a full one for either protocol
# by use of multiple inheritance. (In general, when class C has base
# classes B1...Bn, if x is an instance of class C, methods of x are
# searched first in C, then in B1, then in B2, ..., finally in Bn.)
class TCPMountClient(PartialMountClient, TCPClient):
def init(self, host):
return TCPClient.init(self, host, MOUNTPROG, MOUNTVERS)
class UDPMountClient(PartialMountClient, UDPClient):
def init(self, host):
return UDPClient.init(self, host, MOUNTPROG, MOUNTVERS)
# A little test program for the Mount client. This takes a host as
# command line argument (default the local machine), prints its export
# list, and attempt to mount and unmount each exported files system.
def test():
import sys
if sys.argv[1:]: host = sys.argv[1]
else: host = ''
mcl = UDPMountClient().init(host)
list = mcl.Export()
for item in list:
print item
try:
mcl.Mnt(item[0])
except:
print 'Sorry'
continue
mcl.Umnt(item[0])
return
#test()

207
Demo/rpc/nfsclient.py Normal file
View File

@ -0,0 +1,207 @@
# NFS RPC client -- RFC 1094
# (See mountclient.py for some hints on how to write RPC clients in
# Python in general)
from rpc import UDPClient, TCPClient
from mountclient import FHSIZE, MountPacker, MountUnpacker
NFS_PROGRAM = 100003
NFS_VERSION = 2
# enum stat
NFS_OK = 0
# (...many error values...)
# enum ftype
NFNON = 0
NFREG = 1
NFDIR = 2
NFBLK = 3
NFCHR = 4
NFLNK = 5
class NFSPacker(MountPacker):
def pack_sattrargs(self, sa):
file, attributes = sa
self.pack_fhandle(file)
self.pack_sattr(attributes)
def pack_sattr(self, sa):
mode, uid, gid, size, atime, mtime = sa
self.pack_uint(mode)
self.pack_uint(uid)
self.pack_uint(gid)
self.pack_uint(size)
self.pack_timeval(atime)
self.pack_timeval(mtime)
def pack_diropargs(self, da):
dir, name = da
self.pack_fhandle(dir)
self.pack_string(name)
def pack_readdirargs(self, ra):
dir, cookie, count = ra
self.pack_fhandle(dir)
self.pack_uint(cookie)
self.pack_uint(count)
def pack_timeval(self, tv):
secs, usecs = tv
self.pack_uint(secs)
self.pack_uint(usecs)
class NFSUnpacker(MountUnpacker):
def unpack_readdirres(self):
status = self.unpack_enum()
if status == NFS_OK:
entries = self.unpack_list(self.unpack_entry)
eof = self.unpack_bool()
rest = (entries, eof)
else:
rest = None
return (status, rest)
def unpack_entry(self):
fileid = self.unpack_uint()
name = self.unpack_string()
cookie = self.unpack_uint()
return (fileid, name, cookie)
def unpack_diropres(self):
status = self.unpack_enum()
if status == NFS_OK:
fh = self.unpack_fhandle()
fa = self.unpack_fattr()
rest = (fh, fa)
else:
rest = None
return (status, rest)
def unpack_attrstat(self):
status = self.unpack_enum()
if status == NFS_OK:
attributes = self.unpack_fattr()
else:
attributes = None
return status, attributes
def unpack_fattr(self):
type = self.unpack_enum()
mode = self.unpack_uint()
nlink = self.unpack_uint()
uid = self.unpack_uint()
gid = self.unpack_uint()
size = self.unpack_uint()
blocksize = self.unpack_uint()
rdev = self.unpack_uint()
blocks = self.unpack_uint()
fsid = self.unpack_uint()
fileid = self.unpack_uint()
atime = self.unpack_timeval()
mtime = self.unpack_timeval()
ctime = self.unpack_timeval()
return (type, mode, nlink, uid, gid, size, blocksize, \
rdev, blocks, fsid, fileid, atime, mtime, ctime)
def unpack_timeval(self):
secs = self.unpack_uint()
usecs = self.unpack_uint()
return (secs, usecs)
class NFSClient(UDPClient):
def init(self, host):
return UDPClient.init(self, host, NFS_PROGRAM, NFS_VERSION)
def addpackers(self):
self.packer = NFSPacker().init()
self.unpacker = NFSUnpacker().init('')
def Getattr(self, fh):
self.start_call(1)
self.packer.pack_fhandle(fh)
self.do_call()
as = self.unpacker.unpack_attrstat()
self.end_call()
return as
def Setattr(self, sa):
self.start_call(2)
self.packer.pack_sattrargs(sa)
self.do_call()
as = self.unpacker.unpack_attrstat()
self.end_call()
return as
# Root() is obsolete
def Lookup(self, da):
self.start_call(4)
self.packer.pack_diropargs(da)
self.do_call()
dr = self.unpacker.unpack_diropres()
self.end_call()
return dr
# ...
def Readdir(self, ra):
self.start_call(16)
self.packer.pack_readdirargs(ra)
self.do_call()
rr = self.unpacker.unpack_readdirres()
self.end_call()
return rr
# Shorthand to get the entire contents of a directory
def Listdir(self, dir):
list = []
ra = (dir, 0, 16)
while 1:
(status, rest) = self.Readdir(ra)
if status <> NFS_OK:
break
entries, eof = rest
last_cookie = None
for fileid, name, cookie in entries:
print (fileid, name, cookie) # XXX
list.append(fileid, name)
last_cookie = cookie
if eof or not last_cookie:
break
ra = (ra[0], last_cookie, ra[2])
return list
def test():
import sys
if sys.argv[1:]: host = sys.argv[1]
else: host = ''
if sys.argv[2:]: filesys = sys.argv[2]
else: filesys = None
from mountclient import UDPMountClient, TCPMountClient
mcl = TCPMountClient().init(host)
if filesys == None:
list = mcl.Export()
for item in list:
print item
return
sf = mcl.Mnt(filesys)
print sf
fh = sf[1]
if fh:
ncl = NFSClient().init(host)
as = ncl.Getattr(fh)
print as
list = ncl.Listdir(fh)
for item in list: print item
mcl.Unmnt(filesys)
test()

376
Demo/rpc/rpc.py Normal file
View File

@ -0,0 +1,376 @@
# Implement (a subset of) Sun RPC, version 2 -- RFC1057.
import xdr
import socket
import os
RPCVERSION = 2
CALL = 0
REPLY = 1
AUTH_NULL = 0
AUTH_UNIX = 1
AUTH_SHORT = 2
AUTH_DES = 3
MSG_ACCEPTED = 0
MSG_DENIED = 1
SUCCESS = 0 # RPC executed successfully
PROG_UNAVAIL = 1 # remote hasn't exported program
PROG_MISMATCH = 2 # remote can't support version #
PROC_UNAVAIL = 3 # program can't support procedure
GARBAGE_ARGS = 4 # procedure can't decode params
RPC_MISMATCH = 0 # RPC version number != 2
AUTH_ERROR = 1 # remote can't authenticate caller
AUTH_BADCRED = 1 # bad credentials (seal broken)
AUTH_REJECTEDCRED = 2 # client must begin new session
AUTH_BADVERF = 3 # bad verifier (seal broken)
AUTH_REJECTEDVERF = 4 # verifier expired or replayed
AUTH_TOOWEAK = 5 # rejected for security reasons
class Packer(xdr.Packer):
def pack_auth(self, auth):
flavor, stuff = auth
self.pack_enum(flavor)
self.pack_opaque(stuff)
def pack_auth_unix(self, stamp, machinename, uid, gid, gids):
self.pack_uint(stamp)
self.pack_string(machinename)
self.pack_uint(uid)
self.pack_uint(gid)
self.pack_uint(len(gids))
for i in gids:
self.pack_uint(i)
def pack_callheader(self, xid, prog, vers, proc, cred, verf):
self.pack_uint(xid)
self.pack_enum(CALL)
self.pack_uint(RPCVERSION)
self.pack_uint(prog)
self.pack_uint(vers)
self.pack_uint(proc)
self.pack_auth(cred)
self.pack_auth(verf)
# Caller must add procedure-specific part of call
def pack_replyheader(self, xid, verf):
self.pack_uint(xid)
self.pack_enum(REPLY)
self.pack_uint(MSG_ACCEPTED)
self.pack_auth(verf)
self.pack_enum(SUCCESS)
# Caller must add procedure-specific part of reply
class Unpacker(xdr.Unpacker):
def unpack_auth(self):
flavor = self.unpack_enum()
stuff = self.unpack_opaque()
return (flavor, stuff)
def unpack_replyheader(self):
xid = self.unpack_uint()
mtype = self.unpack_enum()
if mtype <> REPLY:
raise RuntimeError, 'no REPLY but ' + str(mtype)
stat = self.unpack_enum()
if stat <> MSG_ACCEPTED:
if stat == MSG_DENIED:
stat = self.unpack_enum()
if stat == RPC_MISMATCH:
low = self.unpack_uint()
high = self.unpack_uint()
raise 'RPC_MISMATCH', (low, high)
if stat == AUTH_ERROR:
stat = self.unpack_uint()
raise 'AUTH_ERROR', str(stat)
raise 'MSG_REJECTED', str(stat)
raise RuntimeError, 'no MSG_ACCEPTED but ' + str(stat)
verf = self.unpack_auth()
stat = self.unpack_enum()
if stat <> SUCCESS:
raise RuntimeError, 'no SUCCESS but ' + str(stat)
return xid, verf
# Caller must get procedure-specific part of reply
# Common base class for clients
class Client:
def init(self, host, prog, vers, port, type):
self.host = host
self.prog = prog
self.vers = vers
self.port = port
self.type = type
self.sock = socket.socket(socket.AF_INET, type)
self.sock.connect((host, port))
self.lastxid = 0
self.addpackers()
self.cred = None
self.verf = None
return self
def Null(self): # Procedure 0 is always like this
self.start_call(0)
self.do_call(0)
self.end_call()
def close(self):
self.sock.close()
# Functions that may be overridden by specific derived classes
def addpackers(self):
self.packer = Packer().init()
self.unpacker = Unpacker().init('')
def mkcred(self, proc):
if self.cred == None:
p = Packer().init()
p.pack_auth_unix(0, socket.gethostname(), \
os.getuid(), os.getgid(), [])
self.cred = p.get_buf()
return (AUTH_UNIX, self.cred)
def mkverf(self, proc):
return (AUTH_NULL, '')
# Record-Marking standard support
def sendfrag(sock, last, frag):
x = len(frag)
if last: x = x | 0x80000000L
header = (chr(int(x>>24 & 0xff)) + chr(int(x>>16 & 0xff)) + \
chr(int(x>>8 & 0xff)) + chr(int(x & 0xff)))
sock.send(header + frag)
def sendrecord(sock, record):
sendfrag(sock, 1, record)
def recvfrag(sock):
header = sock.recv(4)
x = long(ord(header[0]))<<24 | ord(header[1])<<16 | \
ord(header[2])<<8 | ord(header[3])
last = ((x & 0x80000000) != 0)
n = int(x & 0x7fffffff)
frag = ''
while n > 0:
buf = sock.recv(n)
if not buf: raise EOFError
n = n - len(buf)
frag = frag + buf
return last, frag
def recvrecord(sock):
record = ''
last = 0
while not last:
last, frag = recvfrag(sock)
record = record + frag
return record
# Raw TCP-based client
class RawTCPClient(Client):
def init(self, host, prog, vers, port):
return Client.init(self, host, prog, vers, port, \
socket.SOCK_STREAM)
def start_call(self, proc):
self.lastxid = xid = self.lastxid + 1
cred = self.mkcred(proc)
verf = self.mkverf(proc)
p = self.packer
p.reset()
p.pack_callheader(xid, self.prog, self.vers, proc, cred, verf)
def do_call(self, *rest):
# rest is used for UDP buffer size; ignored for TCP
call = self.packer.get_buf()
sendrecord(self.sock, call)
reply = recvrecord(self.sock)
u = self.unpacker
u.reset(reply)
xid, verf = u.unpack_replyheader()
if xid <> self.lastxid:
# Can't really happen since this is TCP...
raise RuntimeError, 'wrong xid in reply ' + `xid` + \
' instead of ' + `self.lastxid`
def end_call(self):
self.unpacker.done()
# Raw UDP-based client
# XXX This class does not recover from missed/duplicated packets!
class RawUDPClient(Client):
def init(self, host, prog, vers, port):
return Client.init(self, host, prog, vers, port, \
socket.SOCK_DGRAM)
def start_call(self, proc):
self.lastxid = xid = self.lastxid + 1
cred = self.mkcred(proc)
verf = self.mkverf(proc)
p = self.packer
p.reset()
p.pack_callheader(xid, self.prog, self.vers, proc, cred, verf)
def do_call(self, *rest):
if len(rest) == 0:
bufsize = 8192
elif len(rest) > 1:
raise TypeError, 'too many args'
else:
bufsize = rest[0] + 512
call = self.packer.get_buf()
self.sock.send(call)
# XXX What about time-out and retry?
reply = self.sock.recv(bufsize)
u = self.unpacker
u.reset(reply)
xid, verf = u.unpack_replyheader()
if xid <> self.lastxid:
# XXX Should assume it's an old reply
raise RuntimeError, 'wrong xid in reply ' + `xid` + \
' instead of ' + `self.lastxid`
def end_call(self):
self.unpacker.done()
# Port mapper interface
PMAP_PORT = 111
PMAP_PROG = 100000
PMAP_VERS = 2
PMAPPROC_NULL = 0 # (void) -> void
PMAPPROC_SET = 1 # (mapping) -> bool
PMAPPROC_UNSET = 2 # (mapping) -> bool
PMAPPROC_GETPORT = 3 # (mapping) -> unsigned int
PMAPPROC_DUMP = 4 # (void) -> pmaplist
PMAPPROC_CALLIT = 5 # (call_args) -> call_result
# A mapping is (prog, vers, prot, port) and prot is one of:
IPPROTO_TCP = 6
IPPROTO_UDP = 17
# A pmaplist is a variable-length list of mappings, as follows:
# either (1, mapping, pmaplist) or (0).
# A call_args is (prog, vers, proc, args) where args is opaque;
# a call_result is (port, res) where res is opaque.
class PortMapperPacker(Packer):
def pack_mapping(self, mapping):
prog, vers, prot, port = mapping
self.pack_uint(prog)
self.pack_uint(vers)
self.pack_uint(prot)
self.pack_uint(port)
def pack_pmaplist(self, list):
self.pack_list(list, self.pack_mapping)
class PortMapperUnpacker(Unpacker):
def unpack_mapping(self):
prog = self.unpack_uint()
vers = self.unpack_uint()
prot = self.unpack_uint()
port = self.unpack_uint()
return prog, vers, prot, port
def unpack_pmaplist(self):
return self.unpack_list(self.unpack_mapping)
class PartialPortMapperClient:
def addpackers(self):
self.packer = PortMapperPacker().init()
self.unpacker = PortMapperUnpacker().init('')
def Getport(self, mapping):
self.start_call(PMAPPROC_GETPORT)
self.packer.pack_mapping(mapping)
self.do_call(4)
port = self.unpacker.unpack_uint()
self.end_call()
return port
def Dump(self):
self.start_call(PMAPPROC_DUMP)
self.do_call(8192-512)
list = self.unpacker.unpack_pmaplist()
self.end_call()
return list
class TCPPortMapperClient(PartialPortMapperClient, RawTCPClient):
def init(self, host):
return RawTCPClient.init(self, \
host, PMAP_PROG, PMAP_VERS, PMAP_PORT)
class UDPPortMapperClient(PartialPortMapperClient, RawUDPClient):
def init(self, host):
return RawUDPClient.init(self, \
host, PMAP_PROG, PMAP_VERS, PMAP_PORT)
class TCPClient(RawTCPClient):
def init(self, host, prog, vers):
pmap = TCPPortMapperClient().init(host)
port = pmap.Getport((prog, vers, IPPROTO_TCP, 0))
pmap.close()
return RawTCPClient.init(self, host, prog, vers, port)
class UDPClient(RawUDPClient):
def init(self, host, prog, vers):
pmap = UDPPortMapperClient().init(host)
port = pmap.Getport((prog, vers, IPPROTO_UDP, 0))
pmap.close()
return RawUDPClient.init(self, host, prog, vers, port)
def test():
import T
T.TSTART()
pmap = UDPPortMapperClient().init('')
T.TSTOP()
pmap.Null()
T.TSTOP()
list = pmap.Dump()
T.TSTOP()
list.sort()
for prog, vers, prot, port in list:
print prog, vers,
if prot == IPPROTO_TCP: print 'tcp',
elif prot == IPPROTO_UDP: print 'udp',
else: print prot,
print port

141
Demo/rpc/xdr.py Normal file
View File

@ -0,0 +1,141 @@
# Implement (a subset of) Sun XDR -- RFC1014.
import struct
class Packer:
def init(self):
self.reset()
return self
def reset(self):
self.buf = ''
def get_buf(self):
return self.buf
def pack_uint(self, x):
self.buf = self.buf + \
(chr(int(x>>24 & 0xff)) + chr(int(x>>16 & 0xff)) + \
chr(int(x>>8 & 0xff)) + chr(int(x & 0xff)))
if struct.pack('i', 1) == '\0\0\0\1':
def pack_uint(self, x):
self.buf = self.buf + struct.pack('i', x)
pack_int = pack_uint
pack_enum = pack_int
def pack_bool(self, x):
if x: self.buf = self.buf + '\0\0\0\1'
else: self.buf = self.buf + '\0\0\0\0'
def pack_uhyper(self, x):
self.pack_uint(x>>32 & 0xffffffff)
self.pack_uint(x & 0xffffffff)
pack_hyper = pack_uhyper
def pack_fstring(self, n, s):
if n < 0:
raise ValueError, 'fstring size must be nonnegative'
n = ((n+3)/4)*4
data = s[:n]
data = data + (n - len(data)) * '\0'
self.buf = self.buf + data
pack_fopaque = pack_fstring
def pack_string(self, s):
n = len(s)
self.pack_uint(n)
self.pack_fstring(n, s)
pack_opaque = pack_string
def pack_list(self, list, pack_item):
for item in list:
self.pack_uint(1)
pack_item(list)
self.pack_uint(0)
class Unpacker:
def init(self, data):
self.reset(data)
return self
def reset(self, data):
self.buf = data
self.pos = 0
def done(self):
if self.pos < len(self.buf):
raise RuntimeError, 'unextracted data remains'
def unpack_uint(self):
i = self.pos
self.pos = j = i+4
data = self.buf[i:j]
x = long(ord(data[0]))<<24 | ord(data[1])<<16 | \
ord(data[2])<<8 | ord(data[3])
# Return a Python long only if the value is not representable
# as a nonnegative Python int
if x < 0x80000000L: x = int(x)
return x
if struct.unpack('i', '\0\0\0\1') == 1:
def unpack_uint(self):
i = self.pos
self.pos = j = i+4
return struct.unpack('i', self.buf[i:j])
def unpack_int(self):
x = self.unpack_uint()
if x >= 0x80000000L: x = x - 0x100000000L
return int(x)
unpack_enum = unpack_int
unpack_bool = unpack_int
def unpack_uhyper(self):
hi = self.unpack_uint()
lo = self.unpack_uint()
return long(hi)<<32 | lo
def unpack_hyper(self):
x = self.unpack_uhyper()
if x >= 0x8000000000000000L: x = x - 0x10000000000000000L
return x
def unpack_fstring(self, n):
if n < 0:
raise ValueError, 'fstring size must be nonnegative'
i = self.pos
j = i + (n+3)/4*4
if j > len(self.buf):
raise RuntimeError, 'buffer overrun'
self.pos = j
return self.buf[i:i+n]
unpack_fopaque = unpack_fstring
def unpack_string(self):
n = self.unpack_uint()
return self.unpack_fstring(n)
unpack_opaque = unpack_string
def unpack_list(self, unpack_item):
list = []
while 1:
x = self.unpack_uint()
if not x: break
if x <> 1:
raise RuntimeError, \
'0 or 1 expected, got ' + `x`
list.append(unpack_item())
return list