X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=bacula%2Fscripts%2Fdvd-handler.in;h=b90108156ead4f0a4626b69c8d61605e81b89f4f;hb=fc92e04201e428fbf206dbd01518a02490ba50f9;hp=fdae793164e70dc7533d6027a782664f3b35212a;hpb=c59c5d9d832c96789ea30ebe879f741a51ecd75b;p=bacula%2Fbacula diff --git a/bacula/scripts/dvd-handler.in b/bacula/scripts/dvd-handler.in index fdae793164..b90108156e 100644 --- a/bacula/scripts/dvd-handler.in +++ b/bacula/scripts/dvd-handler.in @@ -20,68 +20,29 @@ import popen2 import os +import os.path import errno import sys import re import signal import time +import array +class disk: # Configurable values: -dvdrwmediainfo = "@DVDRWMEDIAINFO@" -growcmd = "@GROWISOFS@" -margin = 10485760 # 10 mb security margin + + dvdrwmediainfo = "@DVDRWMEDIAINFO@" + growcmd = "@GROWISOFS@" + dvdrwformat = "@DVDRWFORMAT@" + dd = "@DD@" + margin = 10485760 # 10 mb security margin -# Comment the following line if you want the tray to be reloaded -# when writing ends. -growcmd += " -use-the-force-luke=notray" + # Comment the following line if you want the tray to be reloaded + # when writing ends. + growcmd += " -use-the-force-luke=notray" # end of configurable values -## Check if we want to allow growisofs to cross the 4gb boundary -def is4gbsupported(): - processi = popen2.Popen4("uname -s -r") - status = processi.wait() - if not os.WIFEXITED(status): -# print "dvd-writepart: Cannot execute uname, allowing to cross the 4gb boundary." - return 1 - if os.WEXITSTATUS(status) != 0: -# print "dvd-writepart: Cannot execute uname, allowing to cross the 4gb boundary." - return 1 - strres = processi.fromchild.readline()[0:-1] - res = strres.split(" ") - if len(res) != 2: -# print "dvd-writepart: Unable to parse uname (" + strres + "), allowing to cross the 4gb boundary." - return 1 - if res[0] != "Linux": -# print "dvd-writepart: The current OS is no Linux, allowing to cross the 4gb boundary." - return 1 - ver = res[1].split(".") - if len(ver) < 3: -# print "dvd-writepart: Unable to parse version string (" + res[1] + "), allowing to cross the 4gb boundary." - return 1 - subver = ver[2].split("-") - - if ((not ver[0].isdigit()) or (not ver[1].isdigit()) or (not subver[0].isdigit())): -# print "dvd-writepart: Unable to parse version string (" + res[1] + "), allowing to cross the 4gb boundary." - return 1 - - if (int(ver[0]) > 2) or (int(ver[1]) > 6) or ((int(ver[0]) == 2) and (int(ver[1]) == 6) and (int(subver[0]) >= 8)): -# print "dvd-writepart: Kernel version >=2.6.8, allowing to cross the 4gb boundary." - return 1 - else: -# print "dvd-writepart: Kernel version <2.6.8, not allowing to cross the 4gb boundary." - return 0 - -class DVDError(Exception): - def __init__(self, errno, value): - self.errno = errno - self.value = value - if self.value[-1] == '\n': - self.value = self.value[0:-1] - def __str__(self): - return str(self.value) + " || errno = " + str(self.errno) + " (" + os.strerror(self.errno & 0x7F) + ")" - -class disk: ############################################################################### # # This class represents DVD disk informations. @@ -113,6 +74,7 @@ class disk: def __init__(self, devicename): self.device = devicename self.disktype = "none" + self.diskmode = "none" self.diskstatus = "none" self.hardwaredevice = "none" self.pid = 0 @@ -122,6 +84,14 @@ class disk: self.freespace_collected = 0 self.mediumtype_collected = 0 + self.growcmd += " -quiet" + + if self.is4gbsupported(): + self.growcmd += " -use-the-force-luke=4gms" + + self.growparams = " -A 'Bacula Data' -input-charset=default -iso-level 3 -pad " + \ + "-p 'dvd-handler / growisofs' -sysid 'BACULADATA' -R" + return def __repr__(self): @@ -134,13 +104,32 @@ class disk: self.collect_mediumtype(); self.me = "Class disk, initialized with device '" + self.device + "'\n" - self.me += "type = '" + self.disktype + "' status = '" + self.diskstatus + "'\n" + self.me += "type = '" + self.disktype + "' mode='" + self.diskmode + "' status = '" + self.diskstatus + "'\n" self.me += " next_session = " + str(self.next_session) + " capacity = " + str(self.capacity) + "\n" self.me += "Hardware device is '" + self.hardwaredevice + "'\n" + self.me += "growcmd = '" + self.growcmd + "'\ngrowparams = '" + self.growparams + "'\n" return self.me + ## Check if we want to allow growisofs to cross the 4gb boundary + def is4gbsupported(self): + processi = popen2.Popen4("uname -s -r") + status = processi.wait() + if not os.WIFEXITED(status): + return 1 + if os.WEXITSTATUS(status) != 0: + return 1 + strres = processi.fromchild.readline()[0:-1] + version = re.search(r"Linux (\d+)\.(\d+)\.(\d+)", strres) + if not version: # Non-Linux: allow + return 1 + + if (int(version.group(1)) > 2) or (int(version.group(2)) > 6) or ((int(version.group(1)) == 2) and (int(version.group(2)) == 6) and (int(version.group(3)) >= 8)): + return 1 + else: + return 0 + def collect_freespace(self): # Collects current free space - self.cmd = growcmd + " -F " + self.device + self.cmd = self.growcmd + " -F " + self.device processi = popen2.Popen4(self.cmd) status = processi.wait() if not os.WIFEXITED(status): @@ -156,7 +145,7 @@ class disk: else: raise DVDError(os.WEXITSTATUS(status), "growisofs returned with an error " + result + ". Please check your are using a patched version of dvd+rw-tools.") next_sess = re.search(r"\snext_session=(\d+)\s", result, re.MULTILINE) - capa = re.search(r"\capacity=(\d+)\s", result, re.MULTILINE) + capa = re.search(r"\scapacity=(\d+)\s", result, re.MULTILINE) if next_sess and capa: self.next_session = long(next_sess.group(1)) @@ -173,18 +162,19 @@ class disk: def collect_mediumtype(self): # Collects current medium type self.lasterror = "" - cmd = dvdrwmediainfo + " " + self.device + cmd = self.dvdrwmediainfo + " " + self.device processi = popen2.Popen4(cmd) status = processi.wait() if not os.WIFEXITED(status): - raise DVDError(dvdrwmediainfo + " process did not exit correctly.") + raise DVDError(0, self.dvdrwmediainfo + " process did not exit correctly.") if os.WEXITSTATUS(status) != 0: - raise DVDError("Cannot get media info from " + dvdrwmediainfo) + raise DVDError(0, "Cannot get media info from " + self.dvdrwmediainfo) return result = processi.fromchild.read() hardware = re.search(r"INQUIRY:\s+(.*)\n", result, re.MULTILINE) mediatype = re.search(r"\sMounted Media:\s+([0-9A-F]{2})h, (\S*)\s", result, re.MULTILINE) + mediamode = re.search(r"\sMounted Media:\s+[0-9A-F]{2}h, \S* (.*)\n", result, re.MULTILINE) status = re.search(r"\sDisc status:\s+(.*)\n", result, re.MULTILINE) if hardware: @@ -193,12 +183,18 @@ class disk: if mediatype: self.disktype = mediatype.group(2) else: - raise DVDError("Media type not found in " + dvdrwmediainfo + " output") + raise DVDError(0, "Media type not found in " + self.dvdrwmediainfo + " output") + + if self.disktype == "DVD-RW": + if mediamode: + self.diskmode = mediamode.group(1) + else: + raise DVDError(0, "Media mode not found for DVD-RW in " + self.dvdrwmediainfo + " output") if status: self.diskstatus = status.group(1) else: - raise DVDError("Disc status not found in " + dvdrwmediainfo + " output") + raise DVDError(0, "Disc status not found in " + self.dvdrwmediainfo + " output") self.mediumtype_collected = 1 @@ -213,9 +209,23 @@ class disk: def is_RW(self): if not self.mediumtype_collected: self.collect_mediumtype(); - return "DVD-RW" == self.disktype or "DVD+RW" == self.disktype or "DVD-RAM" == self.disktype + def is_plus_RW(self): + if not self.mediumtype_collected: + self.collect_mediumtype(); + return "DVD+RW" == self.disktype + + def is_minus_RW(self): + if not self.mediumtype_collected: + self.collect_mediumtype(); + return "DVD-RW" == self.disktype + + def is_restricted_overwrite(self): + if not self.mediumtype_collected: + self.collect_mediumtype(); + return self.diskmode == "Restricted Overwrite" + def is_blank(self): if not self.mediumtype_collected: self.collect_mediumtype(); @@ -226,7 +236,7 @@ class disk: if not self.freespace_collected: self.collect_freespace(); - fr = self.capacity-self.next_session-margin + fr = self.capacity-self.next_session-self.margin if fr < 0: return 0 else: @@ -243,13 +253,18 @@ class disk: sys.exit(1) def write(self, newvol, partfile): - # Blank DVD+/-RW/-RAM when there is no data on it - if newvol and self.is_RW() and self.is_blank(): - print "DVD+/-RW looks brand-new, blank it to fix some DVD-writers bugs." + # Blank DVD+RW when there is no data on it + if newvol and self.is_plus_RW() and self.is_blank(): + print "DVD+RW looks brand-new, blank it to fix some DVD-writers bugs." self.blank() - print "Done, now writing the real part file." + print "Done, now writing the part file." + + if newvol and self.is_minus_RW() and (not self.is_restricted_overwrite()): + print "DVD-RW is in " + self.diskmode + " mode, reformating it to Restricted Overwrite" + self.reformat_minus_RW() + print "Done, now writing the part file." - cmd = growcmd + growparams + cmd = self.growcmd + self.growparams if newvol: cmd += " -Z " else: @@ -273,8 +288,68 @@ class disk: if os.WEXITSTATUS(status) != 0: raise DVDError(os.WEXITSTATUS(status), cmd + " exited with status " + str(os.WEXITSTATUS(status)) + ", signal/status " + str(status)) + def prepare(self): + if not self.is_RW(): + raise DVDError(0, "I won't prepare a non-rewritable medium") + + # Blank DVD+RW when there is no data on it + if self.is_plus_RW() and self.is_blank(): + print "DVD+RW looks brand-new, blank it to fix some DVD-writers bugs." + self.blank() + return # It has been completely blanked: Medium is ready to be used by Bacula + + if self.is_minus_RW() and (not self.is_restricted_overwrite()): + print "DVD-RW is in " + self.diskmode + " mode, reformating it to Restricted Overwrite" + self.reformat_minus_RW() + return # Reformated: Medium is ready to be used by Bacula + + # TODO: Check if /dev/fd/0 and /dev/zero exists, otherwise, run self.blank() + if not os.path.exists("/dev/fd/0") or not os.path.exists("/dev/zero"): + print "/dev/fd/0 or /dev/zero doesn't exist, blank the medium completely." + self.blank() + return + + cmd = self.dd + " if=/dev/zero bs=1024 count=512 | " + self.growcmd + " -Z " + self.device + "=/dev/fd/0" + print "Running " + cmd + oldsig = signal.signal(signal.SIGTERM, self.term_handler) + proc = popen2.Popen4(cmd) + self.pid = proc.pid + status = proc.poll() + while status == -1: + line = proc.fromchild.readline() + while len(line) > 0: + print line, + line = proc.fromchild.readline() + time.sleep(1) + status = proc.poll() + self.pid = 0 + print + signal.signal(signal.SIGTERM, oldsig) + if os.WEXITSTATUS(status) != 0: + raise DVDError(os.WEXITSTATUS(status), cmd + " exited with status " + str(os.WEXITSTATUS(status)) + ", signal/status " + str(status)) + def blank(self): - cmd = growcmd + " -Z " + self.device + "=/dev/zero" + cmd = self.growcmd + " -Z " + self.device + "=/dev/zero" + print "Running " + cmd + oldsig = signal.signal(signal.SIGTERM, self.term_handler) + proc = popen2.Popen4(cmd) + self.pid = proc.pid + status = proc.poll() + while status == -1: + line = proc.fromchild.readline() + while len(line) > 0: + print line, + line = proc.fromchild.readline() + time.sleep(1) + status = proc.poll() + self.pid = 0 + print + signal.signal(signal.SIGTERM, oldsig) + if os.WEXITSTATUS(status) != 0: + raise DVDError(os.WEXITSTATUS(status), cmd + " exited with status " + str(os.WEXITSTATUS(status)) + ", signal/status " + str(status)) + + def reformat_minus_RW(self): + cmd = self.dvdrwformat + " -force " + self.device print "Running " + cmd oldsig = signal.signal(signal.SIGTERM, self.term_handler) proc = popen2.Popen4(cmd) @@ -295,6 +370,15 @@ class disk: # class disk ends here. +class DVDError(Exception): + def __init__(self, errno, value): + self.errno = errno + self.value = value + if self.value[-1] == '\n': + self.value = self.value[0:-1] + def __str__(self): + return str(self.value) + " || errno = " + str(self.errno) + " (" + os.strerror(self.errno & 0x7F) + ")" + def usage(): print "Wrong number of arguments." print """ @@ -303,31 +387,29 @@ Usage: dvd-handler DEVICE test dvd-handler DEVICE free dvd-handler DEVICE write APPEND FILE +dvd-handler DEVICE blank where DEVICE is a device name like /dev/sr0 or /dev/dvd. Operations: -test Scan the device and report the information found. +test Scan the device and report the information found. This operation needs no further arguments. -free Scan the device and report the available space. -write Write a part file to disk. +free Scan the device and report the available space. +write Write a part file to disk. This operation needs two additional arguments. The first indicates to append (0) or restart the disk (1). The second is the file to write. +prepare Prepare a DVD+/-RW for being used by Bacula. + Note: This is only useful if you already have some + non-Bacula data on a medium, and you want to use + it with Bacula. Don't run this on blank media, it + is useless. """ sys.exit(1) if len(sys.argv) < 3: usage() -growcmd += " -quiet" - -if is4gbsupported(): - growcmd += " -use-the-force-luke=4gms" - -growparams = " -A 'Bacula Data' -input-charset=default -iso-level 3 -pad " + \ - "-p 'dvd-handler / growisofs' -sysid 'BACULADATA' -R" - dvd = disk(sys.argv[1]) if "free" == sys.argv[2]: @@ -346,10 +428,25 @@ if "free" == sys.argv[2]: else: print "Wrong number of arguments for free operation." usage() +elif "prepare" == sys.argv[2]: + if len(sys.argv) == 3: + try: + dvd.prepare() + except DVDError, e: + print "Error while preparing medium: ", str(e) + if e.errno != 0: + sys.exit(e.errno & 0x7F) + else: + sys.exit(errno.EPIPE) + else: + print "Medium prepared successfully." + else: + print "Wrong number of arguments for prepare operation." + usage() elif "test" == sys.argv[2]: try: print str(dvd) - print "Empty disk: " + str(dvd.is_empty()) + " ReWritable disk: " + str(dvd.is_RW()) + print "Blank disk: " + str(dvd.is_blank()) + " ReWritable disk: " + str(dvd.is_RW()) print "Free space: " + str(dvd.free()) except DVDError, e: print "Error while getting informations: ", str(e) @@ -370,6 +467,6 @@ elif "write" == sys.argv[2]: usage() sys.exit(1) else: - print "No operation - use test, free or write." + print "No operation - use test, free, prepare or write." print "THIS MIGHT BE A CASE OF DEBUGGING BACULA OR AN ERROR!" sys.exit(0)