3 import os, subprocess, sys, signal, time
4 from cStringIO import StringIO
5 from twisted.python import usage
7 from twisted.python.procutils import which
11 return subprocess.call(["python", loc,], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
17 for maybetwistd in which("twistd"):
18 ret = testtwistd(maybetwistd)
24 for maybetwistd in which("twistd.py"):
25 ret = testtwistd(maybetwistd)
31 maybetwistd = os.path.join(sys.prefix, 'Scripts', 'twistd')
32 ret = testtwistd(maybetwistd)
37 maybetwistd = os.path.join(sys.prefix, 'Scripts', 'twistd.py')
38 ret = testtwistd(maybetwistd)
43 print "Can't find twistd (it comes with Twisted). Aborting."
48 ["multiple", "m", "allow multiple basedirs to be specified at once"],
51 def postOptions(self):
53 raise usage.UsageError("<basedir> parameter is required")
56 self['basedirs'] = [os.path.abspath(os.path.expanduser(b))
57 for b in self.basedirs]
59 def parseArgs(self, *args):
62 self.basedirs.append(self['basedir'])
64 self.basedirs.extend(args)
66 if len(args) == 0 and not self.basedirs:
67 self.basedirs.append(".")
69 self.basedirs.append(args[0])
71 raise usage.UsageError("I wasn't expecting so many arguments")
73 class NoDefaultBasedirMixin(BasedirMixin):
74 def parseArgs(self, *args):
75 # create-client won't default to --basedir=.
78 self.basedirs.append(self['basedir'])
80 self.basedirs.extend(args)
83 self.basedirs.append(args[0])
85 raise usage.UsageError("I wasn't expecting so many arguments")
87 raise usage.UsageError("--basedir must be provided")
89 class StartOptions(BasedirMixin, usage.Options):
91 ["basedir", "C", None, "which directory to start the node in"],
94 class StopOptions(BasedirMixin, usage.Options):
96 ["basedir", "C", None, "which directory to stop the node in"],
99 class RestartOptions(BasedirMixin, usage.Options):
101 ["basedir", "C", None, "which directory to restart the node in"],
104 class CreateClientOptions(NoDefaultBasedirMixin, usage.Options):
106 ["basedir", "C", None, "which directory to create the client in"],
109 class CreateIntroducerOptions(NoDefaultBasedirMixin, usage.Options):
111 ["basedir", "C", None, "which directory to create the introducer in"],
114 class DumpOptions(usage.Options):
116 ["filename", "f", None, "which file to dump"],
119 def parseArgs(self, filename=None):
121 self['filename'] = filename
123 def postOptions(self):
124 if not self['filename']:
125 raise usage.UsageError("<filename> parameter is required")
127 class DumpRootDirnodeOptions(BasedirMixin, usage.Options):
129 ["basedir", "C", None, "the vdrive-server's base directory"],
132 class DumpDirnodeOptions(BasedirMixin, usage.Options):
134 ["uri", "u", None, "the URI of the dirnode to dump."],
135 ["basedir", "C", None, "which directory to create the introducer in"],
138 ["verbose", "v", "be extra noisy (show encrypted data)"],
140 def parseArgs(self, *args):
142 self['uri'] = args[-1]
144 BasedirMixin.parseArgs(self, *args)
146 def postOptions(self):
147 BasedirMixin.postOptions(self)
149 raise usage.UsageError("<uri> parameter is required")
154 from allmydata import client
155 from twisted.application import service
159 application = service.Application("allmydata_client")
160 c.setServiceParent(application)
166 from allmydata import introducer_and_vdrive
167 from twisted.application import service
169 c = introducer_and_vdrive.IntroducerAndVdrive()
171 application = service.Application("allmydata_introducer")
172 c.setServiceParent(application)
175 class Options(usage.Options):
176 synopsis = "Usage: allmydata <command> [command options]"
179 ["quiet", "q", "operate silently"],
183 ["create-client", None, CreateClientOptions, "Create a client node."],
184 ["create-introducer", None, CreateIntroducerOptions, "Create a introducer node."],
185 ["start", None, StartOptions, "Start a node (of any type)."],
186 ["stop", None, StopOptions, "Stop a node."],
187 ["restart", None, RestartOptions, "Restart a node."],
188 ["dump-uri-extension", None, DumpOptions,
189 "Unpack and display the contents of a uri_extension file."],
190 ["dump-root-dirnode", None, DumpRootDirnodeOptions,
191 "Compute most of the URI for the vdrive server's root dirnode."],
192 ["dump-dirnode", None, DumpDirnodeOptions,
193 "Unpack and display the contents of a vdrive DirectoryNode."],
196 def postOptions(self):
197 if not hasattr(self, 'subOptions'):
198 raise usage.UsageError("must specify a command")
200 def runner(argv, run_by_human=True, stdout=sys.stdout, stderr=sys.stderr):
203 config.parseOptions(argv)
204 except usage.error, e:
207 print "%s: %s" % (sys.argv[0], e)
209 c = getattr(config, 'subOptions', config)
213 command = config.subCommand
214 so = config.subOptions
220 if command == "create-client":
221 for basedir in so.basedirs:
222 rc = create_client(basedir, so, stdout, stderr) or rc
223 elif command == "create-introducer":
224 for basedir in so.basedirs:
225 rc = create_introducer(basedir, so, stdout, stderr) or rc
226 elif command == "start":
227 for basedir in so.basedirs:
228 rc = start(basedir, so, stdout, stderr) or rc
229 elif command == "stop":
230 for basedir in so.basedirs:
231 rc = stop(basedir, so, stdout, stderr) or rc
232 elif command == "restart":
233 for basedir in so.basedirs:
234 rc = stop(basedir, so, stdout, stderr) or rc
236 print >>stderr, "not restarting"
238 for basedir in so.basedirs:
239 rc = start(basedir, so, stdout, stderr) or rc
240 elif command == "dump-uri-extension":
241 rc = dump_uri_extension(so, stdout, stderr)
242 elif command == "dump-root-dirnode":
243 rc = dump_root_dirnode(so.basedirs[0], so, stdout, stderr)
244 elif command == "dump-dirnode":
245 rc = dump_directory_node(so.basedirs[0], so, stdout, stderr)
249 rc = runner(sys.argv[1:])
252 def create_client(basedir, config, out=sys.stdout, err=sys.stderr):
253 if os.path.exists(basedir):
254 if os.listdir(basedir):
255 print >>err, "The base directory already exists: %s" % basedir
256 print >>err, "To avoid clobbering anything, I am going to quit now"
257 print >>err, "Please use a different directory, or delete this one"
259 # we're willing to use an empty directory
262 f = open(os.path.join(basedir, "client.tac"), "w")
265 print >>out, "client created in %s" % basedir
266 print >>out, " please copy introducer.furl and vdrive.furl into the directory"
268 def create_introducer(basedir, config, out=sys.stdout, err=sys.stderr):
269 if os.path.exists(basedir):
270 if os.listdir(basedir):
271 print >>err, "The base directory already exists: %s" % basedir
272 print >>err, "To avoid clobbering anything, I am going to quit now"
273 print >>err, "Please use a different directory, or delete this one"
275 # we're willing to use an empty directory
278 f = open(os.path.join(basedir, "introducer.tac"), "w")
279 f.write(introducer_tac)
281 print >>out, "introducer created in %s" % basedir
283 def start(basedir, config, out=sys.stdout, err=sys.stderr):
284 print >>out, "STARTING", basedir
285 if os.path.exists(os.path.join(basedir, "client.tac")):
288 elif os.path.exists(os.path.join(basedir, "introducer.tac")):
289 tac = "introducer.tac"
292 print >>err, "%s does not look like a node directory" % basedir
293 if not os.path.isdir(basedir):
294 print >>err, " in fact, it doesn't look like a directory at all!"
296 rc = subprocess.call(["python", twistd, "-y", tac,], cwd=basedir)
298 print >>out, "%s node probably started" % type
301 print >>err, "%s node probably not started" % type
304 def stop(basedir, config, out=sys.stdout, err=sys.stderr):
305 print >>out, "STOPPING", basedir
306 pidfile = os.path.join(basedir, "twistd.pid")
307 if not os.path.exists(pidfile):
308 print >>err, "%s does not look like a running node directory (no twistd.pid)" % basedir
310 pid = open(pidfile, "r").read()
314 os.kill(pid, signal.SIGTERM)
317 # poll once per second until twistd.pid goes away, up to 5 seconds
321 print >>out, "process %d is dead" % pid
325 print >>err, "never saw process go away"
328 def dump_uri_extension(config, out=sys.stdout, err=sys.stderr):
329 from allmydata import uri
331 filename = config['filename']
332 unpacked = uri.unpack_extension_readable(open(filename,"rb").read())
333 keys1 = ("size", "num_segments", "segment_size",
334 "needed_shares", "total_shares")
335 keys2 = ("codec_name", "codec_params", "tail_codec_params")
336 keys3 = ("plaintext_hash", "plaintext_root_hash",
337 "crypttext_hash", "crypttext_root_hash",
341 print >>out, "%19s: %s" % (k, unpacked[k])
345 print >>out, "%19s: %s" % (k, unpacked[k])
349 print >>out, "%19s: %s" % (k, unpacked[k])
351 leftover = set(unpacked.keys()) - set(keys1 + keys2 + keys3)
354 for k in sorted(leftover):
355 print >>out, "%s: %s" % (k, unpacked[k])
360 def dump_root_dirnode(basedir, config, out=sys.stdout, err=sys.stderr):
361 from allmydata import uri
363 root_dirnode_file = os.path.join(basedir, "vdrive", "root")
365 f = open(root_dirnode_file, "rb")
367 rooturi = uri.pack_dirnode_uri("fakeFURL", key)
370 except EnvironmentError:
371 print >>out, "unable to read root dirnode file from %s" % \
375 def dump_directory_node(basedir, config, out=sys.stdout, err=sys.stderr):
376 from allmydata import filetable, vdrive, uri
377 from allmydata.util import hashutil, idlib
378 dir_uri = config['uri']
379 verbose = config['verbose']
381 furl, key = uri.unpack_dirnode_uri(dir_uri)
382 if uri.is_mutable_dirnode_uri(dir_uri):
383 wk, we, rk, index = hashutil.generate_dirnode_keys_from_writekey(key)
385 wk, we, rk, index = hashutil.generate_dirnode_keys_from_readkey(key)
387 filename = os.path.join(basedir, "vdrive", idlib.b2a(index))
390 print >>out, "dirnode uri: %s" % dir_uri
391 print >>out, "filename : %s" % filename
392 print >>out, "index : %s" % idlib.b2a(index)
394 print >>out, "writekey : %s" % idlib.b2a(wk)
395 print >>out, "write_enabler: %s" % idlib.b2a(we)
397 print >>out, "writekey : None"
398 print >>out, "write_enabler: None"
399 print >>out, "readkey : %s" % idlib.b2a(rk)
403 vds = filetable.VirtualDriveServer(os.path.join(basedir, "vdrive"), False)
404 data = vds._read_from_file(index)
407 print >>out, "ERROR: write_enabler does not match"
409 for (H_key, E_key, E_write, E_read) in data[1]:
411 print >>out, " H_key %s" % idlib.b2a(H_key)
412 print >>out, " E_key %s" % idlib.b2a(E_key)
413 print >>out, " E_write %s" % idlib.b2a(E_write)
414 print >>out, " E_read %s" % idlib.b2a(E_read)
415 key = vdrive.decrypt(rk, E_key)
416 print >>out, " key %s" % key
417 if hashutil.dir_name_hash(rk, key) != H_key:
418 print >>out, " ERROR: H_key does not match"
420 if len(E_write) < 14:
421 print >>out, " ERROR: write data is short:", idlib.b2a(E_write)
422 write = vdrive.decrypt(wk, E_write)
423 print >>out, " write: %s" % write
424 read = vdrive.decrypt(rk, E_read)
425 print >>out, " read: %s" % read