00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031 import os, re, string, sys, types
00032
00033 DEFAULT_PORT = "4832"
00034 DEFAULT_INTERVAL = "5"
00035 DEFAULT_OUT = None
00036 DEFAULT_LOG = "rdd-%u.log" % os.getpid()
00037 DEFAULT_BLKSIZE = "512k"
00038 DEFAULT_MINBLKSIZE = "512"
00039 DEFAULT_MAXERR = 0
00040 DEFAULT_RECOVERYLEN = 3
00041
00042 class Cmd:
00043 def __init__(self):
00044 self.mode = None
00045 self.verb = None
00046 self.progress = None
00047 self.md5 = None
00048 self.sha1 = None
00049 self.host = None
00050 self.src = None
00051 self.dst = None
00052 self.log = None
00053 self.blksize = None
00054 self.offset = None
00055 self.count = None
00056 self.short = "rdd"
00057 self.long = "rdd"
00058 self.minblksize = None
00059 self.maxerr = None
00060
00061 def add_opt(self, sopt, lopt, arg=None):
00062 if arg:
00063 self.short = self.short + (" %s %s" % (sopt, arg))
00064 self.long = self.long + (" %s %s" % (lopt, arg))
00065 else:
00066 self.short = self.short + (" %s" % sopt)
00067 self.long = self.long + (" %s" % lopt)
00068
00069 def add_raw(self, str):
00070 self.short = self.short + (" %s" % str)
00071 self.long = self.long + (" %s" % str)
00072
00073 def build_cmdline(self):
00074 cmd = "rdd"
00075 if self.mode == "client":
00076 self.add_opt("-C", "--client")
00077 elif self.mode == "server":
00078 self.add_opt("-S", "--server")
00079
00080 if self.verb:
00081 self.add_opt("-v", "--verbose")
00082 if self.progress:
00083 self.add_opt("-P", "--progress", self.progress)
00084 if self.md5:
00085 self.add_opt("--md5", "--md5")
00086 if self.sha1:
00087 self.add_opt("--sha1", "--sha1")
00088 if self.blksize:
00089 self.add_opt("-b", "--block-size", self.blksize)
00090 if self.minblksize:
00091 self.add_opt("-m", "--min-block-size", self.minblksize)
00092 if self.maxerr:
00093 self.add_opt("-M", "--max-error", self.maxerr)
00094 if self.offset:
00095 self.add_opt("-o", "--offset", self.offset)
00096 if self.count:
00097 self.add_opt("-c", "--count", self.count)
00098 if self.port:
00099 self.add_opt("-p", "--port", self.port)
00100 if self.log:
00101 self.add_opt("-l", "--log-file", self.log)
00102 if self.src:
00103 self.add_raw(self.src)
00104 if self.host and self.dst:
00105 self.add_raw("%s:%s" % (self.host, self.dst))
00106 elif self.dst:
00107 self.add_raw("%s" % self.dst)
00108
00109 def long_cmd(self):
00110 return self.long
00111
00112 def short_cmd(self):
00113 return self.short
00114
00115 intro_text = """
00116 This is the rdd command-line wizard. It will help you
00117 construct a sensible rdd command line. The wizard will ask
00118 a series of questions and will eventually print a command line
00119 that is based on your answers. Optionally, the wizard will also
00120 run this command.
00121
00122 Type '?' to obtain help information about a question.
00123
00124 Rdd comes with a man page. Type 'man rdd' in another window to read it.
00125 """
00126
00127 size_help = """
00128 The %s is given in bytes. You may use the following
00129 multipliers: b or B (512-byte block or sector); k or K (kilobyte);
00130 m or M (megabyte); g or G (gigabyte). There should be no space between
00131 a number and its multiplier.
00132 """
00133
00134 mode_text = """
00135 In which mode do you want to run rdd [local|client|server]?
00136 """
00137 mode_help = """
00138 In local mode, you can copy data within a single file system.
00139 (If you have NFS mount points, your file system may span
00140 multiple hosts.)
00141
00142 In client mode, you can copy a file across the network to a server host.
00143 On the server host, you must start a server process that will receive
00144 and process the data that you send to it.
00145
00146 In server mode, you can receive a file from an rdd client on
00147 another host.
00148 """
00149
00150 verb_text = """Do you want rdd to be verbose?"""
00151 verb_help = """
00152 In verbose mode, rdd prints more informative messages than it
00153 normally does. This may be useful for debugging a problem or just
00154 for understanding what's happening.
00155 """
00156
00157 source_text = """Input file:"""
00158 source_help = """
00159 This is the name of the file from which data will be read.
00160 """
00161
00162 desthost_text = """Destination host:"""
00163 desthost_help = """
00164 You can specify the destination host's DNS host name (e.g.,
00165 yota.holmes.nl) or its IPv4 address in dotted quad notation (e.g.,
00166 10.5.0.23). The DNS host name will work only if your client
00167 host knows how to resolve DNS host names to IPv4 addresses.
00168 """
00169
00170 port_text = """At which TCP port does the rdd server listen?"""
00171 port_help = """
00172 By default, rdd clients and servers assume that rdd requests
00173 must be sent to TCP port 3482 on the server host. If you
00174 want to use another port, then you should specify another
00175 number, both at the client and the server side. Remember
00176 that ports 0-1023 are reserved for privileged uses. Port
00177 numbers higher than 65535 are invalid.
00178 """
00179
00180 destfile_text = """Output file:"""
00181 destfile_help = """
00182 This is the name of the output file. All directories leading up to
00183 the output file should already exist; rdd will not create missing
00184 directories.
00185
00186 If you do not want to create an output file, just hit ENTER.
00187
00188 If you use output splitting, rdd will prefix the name of each output
00189 file with a sequence number. Do not specify such prefixes manually.
00190 For example, /tmp/disk.img will automatically be converted to
00191 /tmp/000-disk.img, /tmp/001-disk.img, and so on.
00192 """
00193
00194 hash_text = """Hash the data?"""
00195 hash_help = """
00196 A (cryptographic) hash is a fixed-length, digital finger print
00197 of a sequence of bytes that is computed by a well-defined
00198 hash algorithm.
00199 It is very difficult to find two inputs that
00200 have the same hash value (in a reasonable amount of time).
00201
00202 Computing a cryptographic hash over the data allows you to
00203 verify the data's integrity at another time by recomputing
00204 the hash value.
00205
00206 The hash is computed only over the data that is read. Rdd does not
00207 guarantee that the data that it reads will be written to disk (or to the
00208 network correctly). To make sure that you have stored the data correctly,
00209 we recommend that you recompute the hash value over the stored data
00210 and compare it to the hash value computed by rdd. They should be
00211 equal.
00212
00213 You can use the following hash algorithms: MD5 and SHA1.
00214 """
00215
00216 logfile_text = """Log file:"""
00217 logfile_help = """
00218 This is the name of file in which rdd's messages will be logged.
00219 These messages will always be visible on your screen.
00220 If you do not wish to log rdd messages to a file, just hit ENTER.
00221 """
00222
00223 md5_text = """Use MD5?"""
00224 md5_help = """
00225 MD5 is a cryptographic hash algorithm. It generates a 128-bit
00226 hash value from an arbitrary input stream. For a full description, see
00227 RFC 1321. MD5 is widely used, but has known weaknesses. SHA1 is
00228 considered stronger, but is not used as widely. Many hash-value databases
00229 consist only of MD5 hash values.
00230 """
00231
00232 sha1_text = """Use SHA1?"""
00233 sha1_help = """
00234 SHA-1 is a cryptographic hash algorithm. It generates a 160-bit
00235 hash value from an arbitrary input stream. For a full description, see
00236 FIP 180 (a U.S. Federal Information Processing standard).
00237 SHA1 is considered a strong hash algorithm.
00238 """
00239
00240 inetd_text = """Use (x)inetd to start the rdd server process?"""
00241 inetd_help = """
00242 If you say yes here, you will have to configure (x)inetd
00243 to start rdd. Make sure that (x)inetd passes the options
00244 '-S' (server mode) and '-i' (inetd) to rdd.
00245 """
00246
00247 overwrite_text = """Overwrite existing files?"""
00248 overwrite_help = """
00249 If you say no here, rdd will refuse to overwrite existing files.
00250 This is the default behavior, because it prevents silly accidents.
00251 If you say yes, rdd will overwrite existing files without asking
00252 for your confirmation.
00253 """
00254
00255 progress_text = """
00256 How often should rdd report progress [seconds; 0 means never]?
00257 """
00258 progress_help = """
00259 If you say 0 here, rdd will not print periodic progress messages.
00260
00261 If you specify some positive number s, rdd will print a progress
00262 line every s seconds (approximately). The progress line tells you
00263 how much of the data has already been copied and gives the current
00264 copy speed.
00265 """
00266
00267 section_text = """
00268 Do you wish to copy all input data or do you wish to select a section
00269 of the input data?
00270 """
00271 section_help = """
00272 With rdd, you can choose to copy a subsequence of the input data.
00273 You can select a single, contiguous range of bytes.
00274
00275 If you wish to select a subsequence of your input data for
00276 copying, say yes. Otherwise, say no.
00277 """
00278
00279 run_text = """Run now?"""
00280 run_help = """
00281 Type 'yes' to run your rdd command now.
00282 Type 'no' to quit.
00283 """
00284
00285 blksize_text = """Block size?"""
00286 blksize_help = """
00287 The block size specifies how much data rdd will read and write
00288 at a time. The block size should be (significantly) less than
00289 the size of your machine's physical memory. I cannot give
00290 exact guidelines, but very small block sizes will slow down the copying and
00291 very large block sizes waste memory without improving performance.
00292 """ + (size_help % "block size")
00293
00294 minblksize_text = """Minimum block size?"""
00295 minblksize_help = """
00296 When read errors occur, rdd will progressively reduce its block size.
00297 This way, the amount of data lost to read errors is reduced.
00298 You must specify the minimum block size. Rdd will not use blocks
00299 that are smaller than this size. If a read error occurs, at least
00300 this many bytes of data will be lost.
00301 """ + (size_help % "block size")
00302
00303 maxerr_text = """Quit after how many read errors?"""
00304 maxerr_help = """
00305 By default, rdd will not exit after read errors. With this option,
00306 you can force rdd to exit after a specified number of read errors.
00307 If you specify 0, rdd allows infinitely many read errors.
00308 """
00309
00310 slice_text = """Process entire input file?"""
00311 slice_help = """
00312 Say 'yes' if you want to process all bytes in the input file.
00313 Say 'no' if you want to process a subsequence of the input file.
00314 """
00315
00316 offset_text = """Input file offset (in bytes)?"""
00317 offset_help = """
00318 Specify at which input file offset rdd should start reading data.
00319 """ + (size_help % "offset")
00320
00321 count_text = """How many bytes to read?"""
00322 count_help = """
00323 Specify how many input bytes should be read.
00324 """ + (size_help % "count")
00325
00326 recover_text = """Do you want to modify any recovery options?"""
00327 recover_help = """
00328 Recovery options include the minimum recovery block size,
00329 the retry count, and the maximum number of read errors.
00330 """
00331
00332 Q = {
00333 "blksize" : (blksize_text, blksize_help),
00334 "count" : (count_text, count_help),
00335 "destfile" : (destfile_text, destfile_help),
00336 "desthost" : (desthost_text, desthost_help),
00337 "hash" : (hash_text, hash_help),
00338 "logfile" : (logfile_text, logfile_help),
00339 "maxerr" : (maxerr_text, maxerr_help),
00340 "md5" : (md5_text, md5_help),
00341 "minblksize" : (minblksize_text, minblksize_help),
00342 "mode" : (mode_text, mode_help),
00343 "offset" : (offset_text, offset_help),
00344 "port" : (port_text, port_help),
00345 "progress" : (progress_text, progress_help),
00346 "recover" : (recover_text, recover_help),
00347 "run" : (run_text, run_help),
00348 "sha1" : (sha1_text, sha1_help),
00349 "slice" : (slice_text, slice_help),
00350 "source" : (source_text, source_help),
00351 "verb" : (verb_text, verb_help),
00352 }
00353
00354 def ask(elt, answers, default=None):
00355 q, help = Q[elt]
00356 q = string.strip(q)
00357 if default:
00358 q = q + (" [%s]" % default)
00359 q = "\n*** " + q + " "
00360 sys.stdout.write(q)
00361 fp = sys.stdin
00362 while 1:
00363 ans = fp.readline()
00364 ans = string.strip(ans)
00365 if ans == "?":
00366 fprint(help, 4)
00367 elif ans == "":
00368 if default != None:
00369 return default
00370 elif type(answers) == types.StringType:
00371 if re.match(answers, ans, re.I) != None:
00372 return ans
00373 elif type(answers) in (types.TupleType, types.ListType):
00374 ans = string.lower(ans)
00375 if ans in answers:
00376 return ans
00377 else:
00378 assert 0
00379
00380 sys.stdout.write(q)
00381 return ans
00382
00383 def ask_yn(elt, default=None):
00384 ans = ask(elt, ("([yY]|[yY][eE][sS]|[nN]|[nN][oO])"), default)
00385 ans = string.lower(ans)
00386 if ans[0] == "y":
00387 return 1
00388 else:
00389 return 0
00390
00391 def ask_num(elt, default=None):
00392 return ask(elt, "\d+", default)
00393
00394 def ask_file(elt, default=None):
00395 return ask(elt, ".+", default)
00396
00397 def ask_opt_file(elt, default=None):
00398 if default == None:
00399 default = ""
00400 f = ask_file(elt, default)
00401 if len(f) == 0:
00402 return None
00403 else:
00404 return f
00405
00406 def format(str, indent):
00407 spaces = " " * indent
00408 width = 75 - indent
00409 fp_wr, fp_rd = os.popen2("fmt -%u" % width)
00410 fp_wr.write(str)
00411 fp_wr.close()
00412 formatted = ""
00413 for line in fp_rd.readlines():
00414 formatted = formatted + spaces + line
00415 fp_rd.close()
00416 return formatted
00417
00418 def fprint(txt, indent=0):
00419 print string.rstrip(format(txt, indent))
00420
00421 def ask_hash(cmd):
00422 if not ask_yn("hash", "yes"):
00423 cmd.md5 = None
00424 cmd.sha1 = None
00425 return
00426 if not ask_yn("md5", "yes"):
00427 cmd.md5 = None
00428 sha_default = "yes"
00429 else:
00430 cmd.md5 = 1
00431 sha_default = "no"
00432 if not ask_yn("sha1", sha_default):
00433 cmd.sha1 = None
00434 else:
00435 cmd.sha1 = 1
00436
00437 def ask_size(elt, default=None):
00438 return ask(elt, "\d+[bBkKmMgG]?", default)
00439
00440 def ask_recover(cmd):
00441 if ask_yn("recover", "no"):
00442 cmd.blksize = ask_size("minblksize", DEFAULT_MINBLKSIZE)
00443 cmd.max_err = ask_num("maxerr", DEFAULT_MAXERR)
00444
00445 def ask_slice(cmd):
00446 if not ask_yn("slice", "yes"):
00447 cmd.offset = ask_size("offset")
00448 cmd.count = ask_size("count")
00449 else:
00450 cmd.offset = None
00451 cmd.count = None
00452
00453 def toplevel():
00454 global mode, verb, src, dst, host, port, progress, log
00455 global blksize, offset, count
00456
00457 cmd = Cmd()
00458
00459 print
00460 fprint(intro_text)
00461
00462 cmd.mode = ask("mode", ("local", "client", "server"), "local")
00463 cmd.verb = ask_yn("verb", "no")
00464 cmd.progress = ask_num("progress", DEFAULT_INTERVAL)
00465 ask_hash(cmd)
00466
00467 if cmd.mode == "local":
00468 cmd.src = ask_file("source")
00469 cmd.port = None
00470 cmd.host = None
00471 cmd.dst = ask_opt_file("destfile", DEFAULT_OUT)
00472 cmd.log = ask_opt_file("logfile", DEFAULT_LOG)
00473 cmd.blksize = ask_size("blksize", DEFAULT_BLKSIZE)
00474 ask_recover(cmd)
00475 ask_slice(cmd)
00476 elif cmd.mode == "client":
00477 cmd.src = ask_file("source")
00478 cmd.host = ask_file("desthost", "localhost")
00479 cmd.port = ask_num("port", DEFAULT_PORT)
00480 cmd.dst = ask_opt_file("destfile", DEFAULT_OUT)
00481 cmd.log = ask_opt_file("logfile", DEFAULT_LOG)
00482 cmd.blksize = ask_size("blksize", DEFAULT_BLKSIZE)
00483 ask_recover(cmd)
00484 ask_slice(cmd)
00485 elif cmd.mode == "server":
00486 cmd.src = None
00487 cmd.host = None
00488 cmd.port = ask_num("port", DEFAULT_PORT)
00489 cmd.dst = None
00490 cmd.log = ask_opt_file("logfile", DEFAULT_LOG)
00491 cmd.blksize = None
00492 cmd.offset, cmd.count = None, None
00493
00494 cmd.build_cmdline()
00495 cmd_short = cmd.short_cmd()
00496 cmd_long = cmd.long_cmd()
00497 print "Command lines:\n\n\t%s\n\n\t%s" % (cmd_short, cmd_long)
00498
00499 if ask_yn("run", "no"):
00500 os.system(cmd_short)
00501
00502 toplevel()