blob: 269dfbc98c0500825705062c4d2a9c0ca6ac8509 [file] [log] [blame]
Guido van Rossume3cafbe1992-12-14 23:25:04 +00001# Mount RPC client -- RFC 1094 (NFS), Appendix A
2
3# This module demonstrates how to write your own RPC client in Python.
4# Since there is no RPC compiler for Python (yet), you must first
5# create classes derived from Packer and Unpacker to handle the data
6# types for the server you want to interface to. You then write the
7# client class. If you want to support both the TCP and the UDP
8# version of a protocol, use multiple inheritance as shown below.
9
10
Guido van Rossum20f99601992-12-15 20:53:17 +000011import rpc
Guido van Rossume3cafbe1992-12-14 23:25:04 +000012from rpc import Packer, Unpacker, TCPClient, UDPClient
13
Guido van Rossumfd92ac81992-12-20 14:57:17 +000014
15# Program number and version for the mount protocol
Guido van Rossume3cafbe1992-12-14 23:25:04 +000016MOUNTPROG = 100005
17MOUNTVERS = 1
18
Guido van Rossumfd92ac81992-12-20 14:57:17 +000019# Size of the 'fhandle' opaque structure
Guido van Rossume3cafbe1992-12-14 23:25:04 +000020FHSIZE = 32
21
22
23# Packer derived class for Mount protocol clients.
24# The only thing we need to pack beyond basic types is an 'fhandle'
25
26class MountPacker(Packer):
27
28 def pack_fhandle(self, fhandle):
29 self.pack_fopaque(FHSIZE, fhandle)
30
31
32# Unpacker derived class for Mount protocol clients.
33# The important types we need to unpack are fhandle, fhstatus,
34# mountlist and exportlist; mountstruct, exportstruct and groups are
35# used to unpack components of mountlist and exportlist and the
36# corresponding functions are passed as function argument to the
37# generic unpack_list function.
38
39class MountUnpacker(Unpacker):
40
41 def unpack_fhandle(self):
42 return self.unpack_fopaque(FHSIZE)
43
44 def unpack_fhstatus(self):
45 status = self.unpack_uint()
46 if status == 0:
47 fh = self.unpack_fhandle()
48 else:
49 fh = None
50 return status, fh
51
52 def unpack_mountlist(self):
53 return self.unpack_list(self.unpack_mountstruct)
54
55 def unpack_mountstruct(self):
56 hostname = self.unpack_string()
57 directory = self.unpack_string()
58 return (hostname, directory)
59
60 def unpack_exportlist(self):
61 return self.unpack_list(self.unpack_exportstruct)
62
63 def unpack_exportstruct(self):
64 filesys = self.unpack_string()
65 groups = self.unpack_groups()
66 return (filesys, groups)
67
68 def unpack_groups(self):
69 return self.unpack_list(self.unpack_string)
70
71
72# These are the procedures specific to the Mount client class.
73# Think of this as a derived class of either TCPClient or UDPClient.
74
75class PartialMountClient:
76
77 # This method is called by Client.init to initialize
78 # self.packer and self.unpacker
79 def addpackers(self):
80 self.packer = MountPacker().init()
81 self.unpacker = MountUnpacker().init('')
82
Guido van Rossumfd92ac81992-12-20 14:57:17 +000083 # This method is called by Client.init to bind the socket
84 # to a particular network interface and port. We use the
85 # default network interface, but if we're running as root,
86 # we want to bind to a reserved port
87 def bindsocket(self):
88 import os
89 if os.getuid() == 0:
90 port = rpc.bindresvport(self.sock, '')
91 # 'port' is not used
92 else:
93 self.sock.bind(('', 0))
94
95 # This function is called to cough up a suitable
Guido van Rossum20f99601992-12-15 20:53:17 +000096 # authentication object for a call to procedure 'proc'.
Guido van Rossumfd92ac81992-12-20 14:57:17 +000097 def mkcred(self):
Guido van Rossum20f99601992-12-15 20:53:17 +000098 if self.cred == None:
99 self.cred = rpc.AUTH_UNIX, rpc.make_auth_unix_default()
100 return self.cred
101
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000102 # The methods Mnt, Dump etc. each implement one Remote
103 # Procedure Call. Their general structure is
104 # self.start_call(<procedure-number>)
105 # <pack arguments using self.packer>
106 # self.do_call() # This does the actual message exchange
107 # <unpack reply using self.unpacker>
108 # self.end_call()
109 # return <reply>
110 # If the call fails, an exception is raised by do_call().
111 # If the reply does not match what you unpack, an exception is
112 # raised either during unpacking (if you overrun the buffer)
113 # or by end_call() (if you leave values in the buffer).
114 # Calling packer methods with invalid arguments (e.g. if
115 # invalid arguments were passed from outside) will also result
116 # in exceptions during packing.
117
118 def Mnt(self, directory):
119 self.start_call(1)
120 self.packer.pack_string(directory)
121 self.do_call()
122 stat = self.unpacker.unpack_fhstatus()
123 self.end_call()
124 return stat
125
126 def Dump(self):
127 self.start_call(2)
128 self.do_call()
129 list = self.unpacker.unpack_mountlist()
130 self.end_call()
131 return list
132
133 def Umnt(self, directory):
134 self.start_call(3)
135 self.packer.pack_string(directory)
136 self.do_call()
137 self.end_call()
138
139 def Umntall(self):
140 self.start_call(4)
141 self.do_call()
142 self.end_call()
143
144 def Export(self):
145 self.start_call(5)
146 self.do_call()
147 list = self.unpacker.unpack_exportlist()
148 self.end_call()
149 return list
150
151
152# We turn the partial Mount client into a full one for either protocol
153# by use of multiple inheritance. (In general, when class C has base
154# classes B1...Bn, if x is an instance of class C, methods of x are
155# searched first in C, then in B1, then in B2, ..., finally in Bn.)
156
157class TCPMountClient(PartialMountClient, TCPClient):
158
159 def init(self, host):
160 return TCPClient.init(self, host, MOUNTPROG, MOUNTVERS)
161
162
163class UDPMountClient(PartialMountClient, UDPClient):
164
165 def init(self, host):
166 return UDPClient.init(self, host, MOUNTPROG, MOUNTVERS)
167
168
169# A little test program for the Mount client. This takes a host as
170# command line argument (default the local machine), prints its export
Guido van Rossumfd92ac81992-12-20 14:57:17 +0000171# list, and attempts to mount and unmount each exported files system.
172# An optional first argument of -t or -u specifies the protocol to use
173# (TCP or UDP), default is UDP.
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000174
175def test():
176 import sys
Guido van Rossumfd92ac81992-12-20 14:57:17 +0000177 if sys.argv[1:] and sys.argv[1] == '-t':
178 C = TCPMountClient
179 del sys.argv[1]
180 elif sys.argv[1:] and sys.argv[1] == '-u':
181 C = UDPMountClient
182 del sys.argv[1]
183 else:
184 C = UDPMountClient
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000185 if sys.argv[1:]: host = sys.argv[1]
186 else: host = ''
Guido van Rossumfd92ac81992-12-20 14:57:17 +0000187 mcl = C().init(host)
Guido van Rossume3cafbe1992-12-14 23:25:04 +0000188 list = mcl.Export()
189 for item in list:
190 print item
191 try:
192 mcl.Mnt(item[0])
193 except:
194 print 'Sorry'
195 continue
196 mcl.Umnt(item[0])