diff options
author | cinap_lenrek <cinap_lenrek@localhost> | 2011-05-03 11:25:13 +0000 |
---|---|---|
committer | cinap_lenrek <cinap_lenrek@localhost> | 2011-05-03 11:25:13 +0000 |
commit | 458120dd40db6b4df55a4e96b650e16798ef06a0 (patch) | |
tree | 8f82685be24fef97e715c6f5ca4c68d34d5074ee /sys/src/cmd/python/Demo/rpc | |
parent | 3a742c699f6806c1145aea5149bf15de15a0afd7 (diff) |
add hg and python
Diffstat (limited to 'sys/src/cmd/python/Demo/rpc')
-rw-r--r-- | sys/src/cmd/python/Demo/rpc/MANIFEST | 10 | ||||
-rw-r--r-- | sys/src/cmd/python/Demo/rpc/README | 31 | ||||
-rw-r--r-- | sys/src/cmd/python/Demo/rpc/T.py | 22 | ||||
-rw-r--r-- | sys/src/cmd/python/Demo/rpc/mountclient.py | 202 | ||||
-rw-r--r-- | sys/src/cmd/python/Demo/rpc/nfsclient.py | 201 | ||||
-rw-r--r-- | sys/src/cmd/python/Demo/rpc/rnusersclient.py | 98 | ||||
-rw-r--r-- | sys/src/cmd/python/Demo/rpc/rpc.py | 893 | ||||
-rwxr-xr-x | sys/src/cmd/python/Demo/rpc/test | 24 | ||||
-rw-r--r-- | sys/src/cmd/python/Demo/rpc/xdr.py | 200 |
9 files changed, 1681 insertions, 0 deletions
diff --git a/sys/src/cmd/python/Demo/rpc/MANIFEST b/sys/src/cmd/python/Demo/rpc/MANIFEST new file mode 100644 index 000000000..e65f3ebee --- /dev/null +++ b/sys/src/cmd/python/Demo/rpc/MANIFEST @@ -0,0 +1,10 @@ + File Name Archive # Description +----------------------------------------------------------- + MANIFEST 1 This shipping list + README 1 + T.py 1 + mountclient.py 1 + nfsclient.py 1 + rpc.py 1 + test 1 + xdr.py 1 diff --git a/sys/src/cmd/python/Demo/rpc/README b/sys/src/cmd/python/Demo/rpc/README new file mode 100644 index 000000000..97948a338 --- /dev/null +++ b/sys/src/cmd/python/Demo/rpc/README @@ -0,0 +1,31 @@ +This is a Python interface to Sun RPC, designed and implemented mostly +by reading the Internet RFCs about the subject. + +*** NOTE: xdr.py has evolved into the standard module xdrlib.py *** + +There are two library modules, xdr.py and rpc.py, and several example +clients: mountclient.py, nfsclient.py, and rnusersclient.py, +implementing the NFS Mount protocol, (part of) the NFS protocol, and +the "rnusers" protocol (used by rusers(1)), respectively. The latter +demonstrates the use of broadcast via the Port mapper's CALLIT +procedure. + +There is also a way to create servers in Python. + +To test the nfs client, run it from the shell with something like this: + + python -c 'import nfsclient; nfsclient.test()' [hostname [filesystemname]] + +When called without a filesystemname, it lists the filesystems at the +host; default host is the local machine. + +Other clients are tested similarly. + +For hostname, use e.g. wuarchive.wustl.edu or gatekeeper.dec.com (two +hosts that are known to export NFS filesystems with little restrictions). + +There are now two different RPC compilers: + +1) Wim Lewis rpcgen.py found on http://www.omnigroup.com/~wiml/soft/stale-index.html#python. + +2) Peter Åstrands rpcgen.py, which is part of "pynfs" (http://www.cendio.se/~peter/pynfs/). diff --git a/sys/src/cmd/python/Demo/rpc/T.py b/sys/src/cmd/python/Demo/rpc/T.py new file mode 100644 index 000000000..33255073e --- /dev/null +++ b/sys/src/cmd/python/Demo/rpc/T.py @@ -0,0 +1,22 @@ +# Simple interface to report execution times of program fragments. +# Call TSTART() to reset the timer, TSTOP(...) to report times. + +import sys, os, time + +def TSTART(): + global t0, t1 + u, s, cu, cs = os.times() + t0 = u+cu, s+cs, time.time() + +def TSTOP(*label): + global t0, t1 + u, s, cu, cs = os.times() + t1 = u+cu, s+cs, time.time() + 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 + '%r user, %r sys, %r real\n' % (u, s, r) + sys.stderr.write(msg) diff --git a/sys/src/cmd/python/Demo/rpc/mountclient.py b/sys/src/cmd/python/Demo/rpc/mountclient.py new file mode 100644 index 000000000..318a9d99b --- /dev/null +++ b/sys/src/cmd/python/Demo/rpc/mountclient.py @@ -0,0 +1,202 @@ +# Mount RPC client -- RFC 1094 (NFS), Appendix A + +# This module demonstrates how to write your own RPC client in Python. +# When this example was written, there was no RPC compiler for +# Python. Without such a compiler, 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. + + +import rpc +from rpc import Packer, Unpacker, TCPClient, UDPClient + + +# Program number and version for the mount protocol +MOUNTPROG = 100005 +MOUNTVERS = 1 + +# Size of the 'fhandle' opaque structure +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() + self.unpacker = MountUnpacker('') + + # This method is called by Client.__init__ to bind the socket + # to a particular network interface and port. We use the + # default network interface, but if we're running as root, + # we want to bind to a reserved port + def bindsocket(self): + import os + try: + uid = os.getuid() + except AttributeError: + uid = 1 + if uid == 0: + port = rpc.bindresvport(self.sock, '') + # 'port' is not used + else: + self.sock.bind(('', 0)) + + # This function is called to cough up a suitable + # authentication object for a call to procedure 'proc'. + def mkcred(self): + if self.cred == None: + self.cred = rpc.AUTH_UNIX, rpc.make_auth_unix_default() + return self.cred + + # The methods Mnt, Dump etc. each implement one Remote + # Procedure Call. This is done by calling self.make_call() + # with as arguments: + # + # - the procedure number + # - the arguments (or None) + # - the "packer" function for the arguments (or None) + # - the "unpacker" function for the return value (or None) + # + # The packer and unpacker function, if not None, *must* be + # methods of self.packer and self.unpacker, respectively. + # A value of None means that there are no arguments or is no + # return value, respectively. + # + # The return value from make_call() is the return value from + # the remote procedure call, as unpacked by the "unpacker" + # function, or None if the unpacker function is None. + # + # (Even if you expect a result of None, you should still + # return the return value from make_call(), since this may be + # needed by a broadcasting version of the class.) + # + # If the call fails, make_call() raises an exception + # (this includes time-outs and invalid results). + # + # Note that (at least with the UDP protocol) there is no + # guarantee that a call is executed at most once. When you do + # get a reply, you know it has been executed at least once; + # when you don't get a reply, you know nothing. + + def Mnt(self, directory): + return self.make_call(1, directory, \ + self.packer.pack_string, \ + self.unpacker.unpack_fhstatus) + + def Dump(self): + return self.make_call(2, None, \ + None, self.unpacker.unpack_mountlist) + + def Umnt(self, directory): + return self.make_call(3, directory, \ + self.packer.pack_string, None) + + def Umntall(self): + return self.make_call(4, None, None, None) + + def Export(self): + return self.make_call(5, None, \ + None, self.unpacker.unpack_exportlist) + + +# 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): + TCPClient.__init__(self, host, MOUNTPROG, MOUNTVERS) + + +class UDPMountClient(PartialMountClient, UDPClient): + + def __init__(self, host): + 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 attempts to mount and unmount each exported files system. +# An optional first argument of -t or -u specifies the protocol to use +# (TCP or UDP), default is UDP. + +def test(): + import sys + if sys.argv[1:] and sys.argv[1] == '-t': + C = TCPMountClient + del sys.argv[1] + elif sys.argv[1:] and sys.argv[1] == '-u': + C = UDPMountClient + del sys.argv[1] + else: + C = UDPMountClient + if sys.argv[1:]: host = sys.argv[1] + else: host = '' + mcl = C(host) + list = mcl.Export() + for item in list: + print item + try: + mcl.Mnt(item[0]) + except: + print 'Sorry' + continue + mcl.Umnt(item[0]) diff --git a/sys/src/cmd/python/Demo/rpc/nfsclient.py b/sys/src/cmd/python/Demo/rpc/nfsclient.py new file mode 100644 index 000000000..c4387b4a0 --- /dev/null +++ b/sys/src/cmd/python/Demo/rpc/nfsclient.py @@ -0,0 +1,201 @@ +# NFS RPC client -- RFC 1094 + +# XXX This is not yet complete. +# XXX Only GETATTR, SETTTR, LOOKUP and READDIR are supported. + +# (See mountclient.py for some hints on how to write RPC clients in +# Python in general) + +import rpc +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): + UDPClient.__init__(self, host, NFS_PROGRAM, NFS_VERSION) + + def addpackers(self): + self.packer = NFSPacker() + self.unpacker = NFSUnpacker('') + + def mkcred(self): + if self.cred == None: + self.cred = rpc.AUTH_UNIX, rpc.make_auth_unix_default() + return self.cred + + def Getattr(self, fh): + return self.make_call(1, fh, \ + self.packer.pack_fhandle, \ + self.unpacker.unpack_attrstat) + + def Setattr(self, sa): + return self.make_call(2, sa, \ + self.packer.pack_sattrargs, \ + self.unpacker.unpack_attrstat) + + # Root() is obsolete + + def Lookup(self, da): + return self.make_call(4, da, \ + self.packer.pack_diropargs, \ + self.unpacker.unpack_diropres) + + # ... + + def Readdir(self, ra): + return self.make_call(16, ra, \ + self.packer.pack_readdirargs, \ + self.unpacker.unpack_readdirres) + + # Shorthand to get the entire contents of a directory + def Listdir(self, dir): + list = [] + ra = (dir, 0, 2000) + while 1: + (status, rest) = self.Readdir(ra) + if status <> NFS_OK: + break + entries, eof = rest + last_cookie = None + for fileid, name, cookie in entries: + list.append((fileid, name)) + last_cookie = cookie + if eof or last_cookie == None: + 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(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(host) + as = ncl.Getattr(fh) + print as + list = ncl.Listdir(fh) + for item in list: print item + mcl.Umnt(filesys) diff --git a/sys/src/cmd/python/Demo/rpc/rnusersclient.py b/sys/src/cmd/python/Demo/rpc/rnusersclient.py new file mode 100644 index 000000000..1f3a882c5 --- /dev/null +++ b/sys/src/cmd/python/Demo/rpc/rnusersclient.py @@ -0,0 +1,98 @@ +# Remote nusers client interface + +import rpc +from rpc import Packer, Unpacker, UDPClient, BroadcastUDPClient + + +class RnusersPacker(Packer): + def pack_utmp(self, ui): + ut_line, ut_name, ut_host, ut_time = utmp + self.pack_string(ut_line) + self.pack_string(ut_name) + self.pack_string(ut_host) + self.pack_int(ut_time) + def pack_utmpidle(self, ui): + ui_itmp, ui_idle = ui + self.pack_utmp(ui_utmp) + self.pack_uint(ui_idle) + def pack_utmpidlearr(self, list): + self.pack_array(list, self.pack_itmpidle) + + +class RnusersUnpacker(Unpacker): + def unpack_utmp(self): + ut_line = self.unpack_string() + ut_name = self.unpack_string() + ut_host = self.unpack_string() + ut_time = self.unpack_int() + return ut_line, ut_name, ut_host, ut_time + def unpack_utmpidle(self): + ui_utmp = self.unpack_utmp() + ui_idle = self.unpack_uint() + return ui_utmp, ui_idle + def unpack_utmpidlearr(self): + return self.unpack_array(self.unpack_utmpidle) + + +class PartialRnusersClient: + + def addpackers(self): + self.packer = RnusersPacker() + self.unpacker = RnusersUnpacker('') + + def Num(self): + return self.make_call(1, None, None, self.unpacker.unpack_int) + + def Names(self): + return self.make_call(2, None, \ + None, self.unpacker.unpack_utmpidlearr) + + def Allnames(self): + return self.make_call(3, None, \ + None, self.unpacker.unpack_utmpidlearr) + + +class RnusersClient(PartialRnusersClient, UDPClient): + + def __init__(self, host): + UDPClient.__init__(self, host, 100002, 2) + + +class BroadcastRnusersClient(PartialRnusersClient, BroadcastUDPClient): + + def __init__(self, bcastaddr): + BroadcastUDPClient.__init__(self, bcastaddr, 100002, 2) + + +def test(): + import sys + if not sys.argv[1:]: + testbcast() + return + else: + host = sys.argv[1] + c = RnusersClient(host) + list = c.Names() + for (line, name, host, time), idle in list: + line = strip0(line) + name = strip0(name) + host = strip0(host) + print "%r %r %r %s %s" % (name, host, line, time, idle) + +def testbcast(): + c = BroadcastRnusersClient('<broadcast>') + def listit(list, fromaddr): + host, port = fromaddr + print host + '\t:', + for (line, name, host, time), idle in list: + print strip0(name), + print + c.set_reply_handler(listit) + all = c.Names() + print 'Total Count:', len(all) + +def strip0(s): + while s and s[-1] == '\0': s = s[:-1] + return s + +test() diff --git a/sys/src/cmd/python/Demo/rpc/rpc.py b/sys/src/cmd/python/Demo/rpc/rpc.py new file mode 100644 index 000000000..141fe09a5 --- /dev/null +++ b/sys/src/cmd/python/Demo/rpc/rpc.py @@ -0,0 +1,893 @@ +# Sun RPC version 2 -- RFC1057. + +# XXX There should be separate exceptions for the various reasons why +# XXX an RPC can fail, rather than using RuntimeError for everything + +# XXX Need to use class based exceptions rather than string exceptions + +# XXX The UDP version of the protocol resends requests when it does +# XXX not receive a timely reply -- use only for idempotent calls! + +# XXX There is no provision for call timeout on TCP connections + +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 + + +# Exceptions +BadRPCFormat = 'rpc.BadRPCFormat' +BadRPCVersion = 'rpc.BadRPCVersion' +GarbageArgs = 'rpc.GarbageArgs' + +class Unpacker(xdr.Unpacker): + + def unpack_auth(self): + flavor = self.unpack_enum() + stuff = self.unpack_opaque() + return (flavor, stuff) + + def unpack_callheader(self): + xid = self.unpack_uint() + temp = self.unpack_enum() + if temp != CALL: + raise BadRPCFormat, 'no CALL but %r' % (temp,) + temp = self.unpack_uint() + if temp != RPCVERSION: + raise BadRPCVersion, 'bad RPC version %r' % (temp,) + prog = self.unpack_uint() + vers = self.unpack_uint() + proc = self.unpack_uint() + cred = self.unpack_auth() + verf = self.unpack_auth() + return xid, prog, vers, proc, cred, verf + # Caller must add procedure-specific part of call + + def unpack_replyheader(self): + xid = self.unpack_uint() + mtype = self.unpack_enum() + if mtype != REPLY: + raise RuntimeError, 'no REPLY but %r' % (mtype,) + stat = self.unpack_enum() + if stat == MSG_DENIED: + stat = self.unpack_enum() + if stat == RPC_MISMATCH: + low = self.unpack_uint() + high = self.unpack_uint() + raise RuntimeError, \ + 'MSG_DENIED: RPC_MISMATCH: %r' % ((low, high),) + if stat == AUTH_ERROR: + stat = self.unpack_uint() + raise RuntimeError, \ + 'MSG_DENIED: AUTH_ERROR: %r' % (stat,) + raise RuntimeError, 'MSG_DENIED: %r' % (stat,) + if stat != MSG_ACCEPTED: + raise RuntimeError, \ + 'Neither MSG_DENIED nor MSG_ACCEPTED: %r' % (stat,) + verf = self.unpack_auth() + stat = self.unpack_enum() + if stat == PROG_UNAVAIL: + raise RuntimeError, 'call failed: PROG_UNAVAIL' + if stat == PROG_MISMATCH: + low = self.unpack_uint() + high = self.unpack_uint() + raise RuntimeError, \ + 'call failed: PROG_MISMATCH: %r' % ((low, high),) + if stat == PROC_UNAVAIL: + raise RuntimeError, 'call failed: PROC_UNAVAIL' + if stat == GARBAGE_ARGS: + raise RuntimeError, 'call failed: GARBAGE_ARGS' + if stat != SUCCESS: + raise RuntimeError, 'call failed: %r' % (stat,) + return xid, verf + # Caller must get procedure-specific part of reply + + +# Subroutines to create opaque authentication objects + +def make_auth_null(): + return '' + +def make_auth_unix(seed, host, uid, gid, groups): + p = Packer() + p.pack_auth_unix(seed, host, uid, gid, groups) + return p.get_buf() + +def make_auth_unix_default(): + try: + from os import getuid, getgid + uid = getuid() + gid = getgid() + except ImportError: + uid = gid = 0 + import time + return make_auth_unix(int(time.time()-unix_epoch()), \ + socket.gethostname(), uid, gid, []) + +_unix_epoch = -1 +def unix_epoch(): + """Very painful calculation of when the Unix Epoch is. + + This is defined as the return value of time.time() on Jan 1st, + 1970, 00:00:00 GMT. + + On a Unix system, this should always return 0.0. On a Mac, the + calculations are needed -- and hard because of integer overflow + and other limitations. + + """ + global _unix_epoch + if _unix_epoch >= 0: return _unix_epoch + import time + now = time.time() + localt = time.localtime(now) # (y, m, d, hh, mm, ss, ..., ..., ...) + gmt = time.gmtime(now) + offset = time.mktime(localt) - time.mktime(gmt) + y, m, d, hh, mm, ss = 1970, 1, 1, 0, 0, 0 + offset, ss = divmod(ss + offset, 60) + offset, mm = divmod(mm + offset, 60) + offset, hh = divmod(hh + offset, 24) + d = d + offset + _unix_epoch = time.mktime((y, m, d, hh, mm, ss, 0, 0, 0)) + print "Unix epoch:", time.ctime(_unix_epoch) + return _unix_epoch + + +# Common base class for clients + +class Client: + + def __init__(self, host, prog, vers, port): + self.host = host + self.prog = prog + self.vers = vers + self.port = port + self.makesocket() # Assigns to self.sock + self.bindsocket() + self.connsocket() + self.lastxid = 0 # XXX should be more random? + self.addpackers() + self.cred = None + self.verf = None + + def close(self): + self.sock.close() + + def makesocket(self): + # This MUST be overridden + raise RuntimeError, 'makesocket not defined' + + def connsocket(self): + # Override this if you don't want/need a connection + self.sock.connect((self.host, self.port)) + + def bindsocket(self): + # Override this to bind to a different port (e.g. reserved) + self.sock.bind(('', 0)) + + def addpackers(self): + # Override this to use derived classes from Packer/Unpacker + self.packer = Packer() + self.unpacker = Unpacker('') + + def make_call(self, proc, args, pack_func, unpack_func): + # Don't normally override this (but see Broadcast) + if pack_func is None and args is not None: + raise TypeError, 'non-null args with null pack_func' + self.start_call(proc) + if pack_func: + pack_func(args) + self.do_call() + if unpack_func: + result = unpack_func() + else: + result = None + self.unpacker.done() + return result + + def start_call(self, proc): + # Don't override this + self.lastxid = xid = self.lastxid + 1 + cred = self.mkcred() + verf = self.mkverf() + p = self.packer + p.reset() + p.pack_callheader(xid, self.prog, self.vers, proc, cred, verf) + + def do_call(self): + # This MUST be overridden + raise RuntimeError, 'do_call not defined' + + def mkcred(self): + # Override this to use more powerful credentials + if self.cred == None: + self.cred = (AUTH_NULL, make_auth_null()) + return self.cred + + def mkverf(self): + # Override this to use a more powerful verifier + if self.verf == None: + self.verf = (AUTH_NULL, make_auth_null()) + return self.verf + + def call_0(self): # Procedure 0 is always like this + return self.make_call(0, None, None, None) + + +# 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) + if len(header) < 4: + raise EOFError + 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 + + +# Try to bind to a reserved port (must be root) + +last_resv_port_tried = None +def bindresvport(sock, host): + global last_resv_port_tried + FIRST, LAST = 600, 1024 # Range of ports to try + if last_resv_port_tried == None: + import os + last_resv_port_tried = FIRST + os.getpid() % (LAST-FIRST) + for i in range(last_resv_port_tried, LAST) + \ + range(FIRST, last_resv_port_tried): + last_resv_port_tried = i + try: + sock.bind((host, i)) + return last_resv_port_tried + except socket.error, (errno, msg): + if errno != 114: + raise socket.error, (errno, msg) + raise RuntimeError, 'can\'t assign reserved port' + + +# Client using TCP to a specific port + +class RawTCPClient(Client): + + def makesocket(self): + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + def do_call(self): + 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 %r instead of %r' % ( + xid, self.lastxid) + + +# Client using UDP to a specific port + +class RawUDPClient(Client): + + def makesocket(self): + self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + + def do_call(self): + call = self.packer.get_buf() + self.sock.send(call) + try: + from select import select + except ImportError: + print 'WARNING: select not found, RPC may hang' + select = None + BUFSIZE = 8192 # Max UDP buffer size + timeout = 1 + count = 5 + while 1: + r, w, x = [self.sock], [], [] + if select: + r, w, x = select(r, w, x, timeout) + if self.sock not in r: + count = count - 1 + if count < 0: raise RuntimeError, 'timeout' + if timeout < 25: timeout = timeout *2 +## print 'RESEND', timeout, count + self.sock.send(call) + continue + reply = self.sock.recv(BUFSIZE) + u = self.unpacker + u.reset(reply) + xid, verf = u.unpack_replyheader() + if xid != self.lastxid: +## print 'BAD xid' + continue + break + + +# Client using UDP broadcast to a specific port + +class RawBroadcastUDPClient(RawUDPClient): + + def __init__(self, bcastaddr, prog, vers, port): + RawUDPClient.__init__(self, bcastaddr, prog, vers, port) + self.reply_handler = None + self.timeout = 30 + + def connsocket(self): + # Don't connect -- use sendto + self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) + + def set_reply_handler(self, reply_handler): + self.reply_handler = reply_handler + + def set_timeout(self, timeout): + self.timeout = timeout # Use None for infinite timeout + + def make_call(self, proc, args, pack_func, unpack_func): + if pack_func is None and args is not None: + raise TypeError, 'non-null args with null pack_func' + self.start_call(proc) + if pack_func: + pack_func(args) + call = self.packer.get_buf() + self.sock.sendto(call, (self.host, self.port)) + try: + from select import select + except ImportError: + print 'WARNING: select not found, broadcast will hang' + select = None + BUFSIZE = 8192 # Max UDP buffer size (for reply) + replies = [] + if unpack_func is None: + def dummy(): pass + unpack_func = dummy + while 1: + r, w, x = [self.sock], [], [] + if select: + if self.timeout is None: + r, w, x = select(r, w, x) + else: + r, w, x = select(r, w, x, self.timeout) + if self.sock not in r: + break + reply, fromaddr = self.sock.recvfrom(BUFSIZE) + u = self.unpacker + u.reset(reply) + xid, verf = u.unpack_replyheader() + if xid != self.lastxid: +## print 'BAD xid' + continue + reply = unpack_func() + self.unpacker.done() + replies.append((reply, fromaddr)) + if self.reply_handler: + self.reply_handler(reply, fromaddr) + return replies + + +# Port mapper interface + +# Program number, version and (fixed!) port number +PMAP_PROG = 100000 +PMAP_VERS = 2 +PMAP_PORT = 111 + +# Procedure numbers +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) + + def pack_call_args(self, ca): + prog, vers, proc, args = ca + self.pack_uint(prog) + self.pack_uint(vers) + self.pack_uint(proc) + self.pack_opaque(args) + + +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) + + def unpack_call_result(self): + port = self.unpack_uint() + res = self.unpack_opaque() + return port, res + + +class PartialPortMapperClient: + + def addpackers(self): + self.packer = PortMapperPacker() + self.unpacker = PortMapperUnpacker('') + + def Set(self, mapping): + return self.make_call(PMAPPROC_SET, mapping, \ + self.packer.pack_mapping, \ + self.unpacker.unpack_uint) + + def Unset(self, mapping): + return self.make_call(PMAPPROC_UNSET, mapping, \ + self.packer.pack_mapping, \ + self.unpacker.unpack_uint) + + def Getport(self, mapping): + return self.make_call(PMAPPROC_GETPORT, mapping, \ + self.packer.pack_mapping, \ + self.unpacker.unpack_uint) + + def Dump(self): + return self.make_call(PMAPPROC_DUMP, None, \ + None, \ + self.unpacker.unpack_pmaplist) + + def Callit(self, ca): + return self.make_call(PMAPPROC_CALLIT, ca, \ + self.packer.pack_call_args, \ + self.unpacker.unpack_call_result) + + +class TCPPortMapperClient(PartialPortMapperClient, RawTCPClient): + + def __init__(self, host): + RawTCPClient.__init__(self, \ + host, PMAP_PROG, PMAP_VERS, PMAP_PORT) + + +class UDPPortMapperClient(PartialPortMapperClient, RawUDPClient): + + def __init__(self, host): + RawUDPClient.__init__(self, \ + host, PMAP_PROG, PMAP_VERS, PMAP_PORT) + + +class BroadcastUDPPortMapperClient(PartialPortMapperClient, \ + RawBroadcastUDPClient): + + def __init__(self, bcastaddr): + RawBroadcastUDPClient.__init__(self, \ + bcastaddr, PMAP_PROG, PMAP_VERS, PMAP_PORT) + + +# Generic clients that find their server through the Port mapper + +class TCPClient(RawTCPClient): + + def __init__(self, host, prog, vers): + pmap = TCPPortMapperClient(host) + port = pmap.Getport((prog, vers, IPPROTO_TCP, 0)) + pmap.close() + if port == 0: + raise RuntimeError, 'program not registered' + RawTCPClient.__init__(self, host, prog, vers, port) + + +class UDPClient(RawUDPClient): + + def __init__(self, host, prog, vers): + pmap = UDPPortMapperClient(host) + port = pmap.Getport((prog, vers, IPPROTO_UDP, 0)) + pmap.close() + if port == 0: + raise RuntimeError, 'program not registered' + RawUDPClient.__init__(self, host, prog, vers, port) + + +class BroadcastUDPClient(Client): + + def __init__(self, bcastaddr, prog, vers): + self.pmap = BroadcastUDPPortMapperClient(bcastaddr) + self.pmap.set_reply_handler(self.my_reply_handler) + self.prog = prog + self.vers = vers + self.user_reply_handler = None + self.addpackers() + + def close(self): + self.pmap.close() + + def set_reply_handler(self, reply_handler): + self.user_reply_handler = reply_handler + + def set_timeout(self, timeout): + self.pmap.set_timeout(timeout) + + def my_reply_handler(self, reply, fromaddr): + port, res = reply + self.unpacker.reset(res) + result = self.unpack_func() + self.unpacker.done() + self.replies.append((result, fromaddr)) + if self.user_reply_handler is not None: + self.user_reply_handler(result, fromaddr) + + def make_call(self, proc, args, pack_func, unpack_func): + self.packer.reset() + if pack_func: + pack_func(args) + if unpack_func is None: + def dummy(): pass + self.unpack_func = dummy + else: + self.unpack_func = unpack_func + self.replies = [] + packed_args = self.packer.get_buf() + dummy_replies = self.pmap.Callit( \ + (self.prog, self.vers, proc, packed_args)) + return self.replies + + +# Server classes + +# These are not symmetric to the Client classes +# XXX No attempt is made to provide authorization hooks yet + +class Server: + + def __init__(self, host, prog, vers, port): + self.host = host # Should normally be '' for default interface + self.prog = prog + self.vers = vers + self.port = port # Should normally be 0 for random port + self.makesocket() # Assigns to self.sock and self.prot + self.bindsocket() + self.host, self.port = self.sock.getsockname() + self.addpackers() + + def register(self): + mapping = self.prog, self.vers, self.prot, self.port + p = TCPPortMapperClient(self.host) + if not p.Set(mapping): + raise RuntimeError, 'register failed' + + def unregister(self): + mapping = self.prog, self.vers, self.prot, self.port + p = TCPPortMapperClient(self.host) + if not p.Unset(mapping): + raise RuntimeError, 'unregister failed' + + def handle(self, call): + # Don't use unpack_header but parse the header piecewise + # XXX I have no idea if I am using the right error responses! + self.unpacker.reset(call) + self.packer.reset() + xid = self.unpacker.unpack_uint() + self.packer.pack_uint(xid) + temp = self.unpacker.unpack_enum() + if temp != CALL: + return None # Not worthy of a reply + self.packer.pack_uint(REPLY) + temp = self.unpacker.unpack_uint() + if temp != RPCVERSION: + self.packer.pack_uint(MSG_DENIED) + self.packer.pack_uint(RPC_MISMATCH) + self.packer.pack_uint(RPCVERSION) + self.packer.pack_uint(RPCVERSION) + return self.packer.get_buf() + self.packer.pack_uint(MSG_ACCEPTED) + self.packer.pack_auth((AUTH_NULL, make_auth_null())) + prog = self.unpacker.unpack_uint() + if prog != self.prog: + self.packer.pack_uint(PROG_UNAVAIL) + return self.packer.get_buf() + vers = self.unpacker.unpack_uint() + if vers != self.vers: + self.packer.pack_uint(PROG_MISMATCH) + self.packer.pack_uint(self.vers) + self.packer.pack_uint(self.vers) + return self.packer.get_buf() + proc = self.unpacker.unpack_uint() + methname = 'handle_' + repr(proc) + try: + meth = getattr(self, methname) + except AttributeError: + self.packer.pack_uint(PROC_UNAVAIL) + return self.packer.get_buf() + cred = self.unpacker.unpack_auth() + verf = self.unpacker.unpack_auth() + try: + meth() # Unpack args, call turn_around(), pack reply + except (EOFError, GarbageArgs): + # Too few or too many arguments + self.packer.reset() + self.packer.pack_uint(xid) + self.packer.pack_uint(REPLY) + self.packer.pack_uint(MSG_ACCEPTED) + self.packer.pack_auth((AUTH_NULL, make_auth_null())) + self.packer.pack_uint(GARBAGE_ARGS) + return self.packer.get_buf() + + def turn_around(self): + try: + self.unpacker.done() + except RuntimeError: + raise GarbageArgs + self.packer.pack_uint(SUCCESS) + + def handle_0(self): # Handle NULL message + self.turn_around() + + def makesocket(self): + # This MUST be overridden + raise RuntimeError, 'makesocket not defined' + + def bindsocket(self): + # Override this to bind to a different port (e.g. reserved) + self.sock.bind((self.host, self.port)) + + def addpackers(self): + # Override this to use derived classes from Packer/Unpacker + self.packer = Packer() + self.unpacker = Unpacker('') + + +class TCPServer(Server): + + def makesocket(self): + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.prot = IPPROTO_TCP + + def loop(self): + self.sock.listen(0) + while 1: + self.session(self.sock.accept()) + + def session(self, connection): + sock, (host, port) = connection + while 1: + try: + call = recvrecord(sock) + except EOFError: + break + except socket.error, msg: + print 'socket error:', msg + break + reply = self.handle(call) + if reply is not None: + sendrecord(sock, reply) + + def forkingloop(self): + # Like loop but uses forksession() + self.sock.listen(0) + while 1: + self.forksession(self.sock.accept()) + + def forksession(self, connection): + # Like session but forks off a subprocess + import os + # Wait for deceased children + try: + while 1: + pid, sts = os.waitpid(0, 1) + except os.error: + pass + pid = None + try: + pid = os.fork() + if pid: # Parent + connection[0].close() + return + # Child + self.session(connection) + finally: + # Make sure we don't fall through in the parent + if pid == 0: + os._exit(0) + + +class UDPServer(Server): + + def makesocket(self): + self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self.prot = IPPROTO_UDP + + def loop(self): + while 1: + self.session() + + def session(self): + call, host_port = self.sock.recvfrom(8192) + reply = self.handle(call) + if reply != None: + self.sock.sendto(reply, host_port) + + +# Simple test program -- dump local portmapper status + +def test(): + pmap = UDPPortMapperClient('') + list = pmap.Dump() + 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 + + +# Test program for broadcast operation -- dump everybody's portmapper status + +def testbcast(): + import sys + if sys.argv[1:]: + bcastaddr = sys.argv[1] + else: + bcastaddr = '<broadcast>' + def rh(reply, fromaddr): + host, port = fromaddr + print host + '\t' + repr(reply) + pmap = BroadcastUDPPortMapperClient(bcastaddr) + pmap.set_reply_handler(rh) + pmap.set_timeout(5) + replies = pmap.Getport((100002, 1, IPPROTO_UDP, 0)) + + +# Test program for server, with corresponding client +# On machine A: python -c 'import rpc; rpc.testsvr()' +# On machine B: python -c 'import rpc; rpc.testclt()' A +# (A may be == B) + +def testsvr(): + # Simple test class -- proc 1 doubles its string argument as reply + class S(UDPServer): + def handle_1(self): + arg = self.unpacker.unpack_string() + self.turn_around() + print 'RPC function 1 called, arg', repr(arg) + self.packer.pack_string(arg + arg) + # + s = S('', 0x20000000, 1, 0) + try: + s.unregister() + except RuntimeError, msg: + print 'RuntimeError:', msg, '(ignored)' + s.register() + print 'Service started...' + try: + s.loop() + finally: + s.unregister() + print 'Service interrupted.' + + +def testclt(): + import sys + if sys.argv[1:]: host = sys.argv[1] + else: host = '' + # Client for above server + class C(UDPClient): + def call_1(self, arg): + return self.make_call(1, arg, \ + self.packer.pack_string, \ + self.unpacker.unpack_string) + c = C(host, 0x20000000, 1) + print 'making call...' + reply = c.call_1('hello, world, ') + print 'call returned', repr(reply) diff --git a/sys/src/cmd/python/Demo/rpc/test b/sys/src/cmd/python/Demo/rpc/test new file mode 100755 index 000000000..ba220f24a --- /dev/null +++ b/sys/src/cmd/python/Demo/rpc/test @@ -0,0 +1,24 @@ +: ${PYTHON=python} +: ${SERVER=charon.cwi.nl} + +set -xe + +$PYTHON -c 'from rpc import test; test()' +$PYTHON -c 'from rpc import test; test()' ${SERVER} + +$PYTHON -c 'from rpc import testsvr; testsvr()' & +PID=$! +sleep 2 +$PYTHON -c 'from rpc import testclt; testclt()' +kill -2 $PID + +$PYTHON -c 'from mountclient import test; test()' +$PYTHON -c 'from mountclient import test; test()' gatekeeper.dec.com + +$PYTHON -c 'from nfsclient import test; test()' +$PYTHON -c 'from nfsclient import test; test()' gatekeeper.dec.com +$PYTHON -c 'from nfsclient import test; test()' gatekeeper.dec.com /archive + +$PYTHON -c 'from rnusersclient import test; test()' '' + +$PYTHON -c 'from rpc import testbcast; testbcast()' diff --git a/sys/src/cmd/python/Demo/rpc/xdr.py b/sys/src/cmd/python/Demo/rpc/xdr.py new file mode 100644 index 000000000..df5cbafd0 --- /dev/null +++ b/sys/src/cmd/python/Demo/rpc/xdr.py @@ -0,0 +1,200 @@ +# Implement (a subset of) Sun XDR -- RFC1014. + + +try: + import struct +except ImportError: + struct = None + + +Long = type(0L) + + +class Packer: + + def __init__(self): + self.reset() + + 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 and struct.pack('l', 1) == '\0\0\0\1': + def pack_uint(self, x): + if type(x) == Long: + x = int((x + 0x80000000L) % 0x100000000L \ + - 0x80000000L) + self.buf = self.buf + struct.pack('l', 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(int(x>>32 & 0xffffffff)) + self.pack_uint(int(x & 0xffffffff)) + + pack_hyper = pack_uhyper + + def pack_float(self, x): + # XXX + self.buf = self.buf + struct.pack('f', x) + + def pack_double(self, x): + # XXX + self.buf = self.buf + struct.pack('d', x) + + 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(item) + self.pack_uint(0) + + def pack_farray(self, n, list, pack_item): + if len(list) <> n: + raise ValueError, 'wrong array size' + for item in list: + pack_item(item) + + def pack_array(self, list, pack_item): + n = len(list) + self.pack_uint(n) + self.pack_farray(n, list, pack_item) + + +class Unpacker: + + def __init__(self, data): + self.reset(data) + + 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] + if len(data) < 4: + raise EOFError + 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 and struct.unpack('l', '\0\0\0\1') == 1: + def unpack_uint(self): + i = self.pos + self.pos = j = i+4 + data = self.buf[i:j] + if len(data) < 4: + raise EOFError + return struct.unpack('l', data) + + 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_float(self): + # XXX + i = self.pos + self.pos = j = i+4 + data = self.buf[i:j] + if len(data) < 4: + raise EOFError + return struct.unpack('f', data)[0] + + def unpack_double(self): + # XXX + i = self.pos + self.pos = j = i+8 + data = self.buf[i:j] + if len(data) < 8: + raise EOFError + return struct.unpack('d', data)[0] + + 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 EOFError + 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 x == 0: break + if x <> 1: + raise RuntimeError, '0 or 1 expected, got %r' % (x, ) + item = unpack_item() + list.append(item) + return list + + def unpack_farray(self, n, unpack_item): + list = [] + for i in range(n): + list.append(unpack_item()) + return list + + def unpack_array(self, unpack_item): + n = self.unpack_uint() + return self.unpack_farray(n, unpack_item) |