134c3bf9eSBrad Bishop#!/usr/bin/env python 240a360c2SBrad Bishop 340a360c2SBrad Bishopimport gobject 440a360c2SBrad Bishopimport dbus 540a360c2SBrad Bishopimport dbus.service 640a360c2SBrad Bishopimport dbus.mainloop.glib 70c8c5d4aSMilton Millerimport subprocess 80c8c5d4aSMilton Millerimport tempfile 940a360c2SBrad Bishopimport shutil 1040a360c2SBrad Bishopimport tarfile 1140a360c2SBrad Bishopimport os 1240a360c2SBrad Bishopfrom obmc.dbuslib.bindings import get_dbus, DbusProperties, DbusObjectManager 1340a360c2SBrad Bishop 1440a360c2SBrad BishopDBUS_NAME = 'org.openbmc.control.BmcFlash' 1540a360c2SBrad BishopOBJ_NAME = '/org/openbmc/control/flash/bmc' 1640a360c2SBrad BishopDOWNLOAD_INTF = 'org.openbmc.managers.Download' 1740a360c2SBrad Bishop 180c8c5d4aSMilton MillerBMC_DBUS_NAME = 'org.openbmc.control.Bmc' 190c8c5d4aSMilton MillerBMC_OBJ_NAME = '/org/openbmc/control/bmc0' 200c8c5d4aSMilton Miller 2140a360c2SBrad BishopUPDATE_PATH = '/run/initramfs' 2240a360c2SBrad Bishop 2340a360c2SBrad Bishop 2440a360c2SBrad Bishopdef doExtract(members, files): 2540a360c2SBrad Bishop for tarinfo in members: 2634c3bf9eSBrad Bishop if tarinfo.name in files: 2740a360c2SBrad Bishop yield tarinfo 2840a360c2SBrad Bishop 2940a360c2SBrad Bishop 30*3fc6b790SMilton Millerdef save_fw_env(): 31*3fc6b790SMilton Miller fw_env = "/etc/fw_env.config" 32*3fc6b790SMilton Miller lines = 0 33*3fc6b790SMilton Miller files=[] 34*3fc6b790SMilton Miller envcfg = open(fw_env, 'r') 35*3fc6b790SMilton Miller try: 36*3fc6b790SMilton Miller for line in envcfg.readlines(): 37*3fc6b790SMilton Miller # ignore lines that are blank or start with # 38*3fc6b790SMilton Miller if (line.startswith("#")): continue 39*3fc6b790SMilton Miller if (not len(line.strip())): continue 40*3fc6b790SMilton Miller fn = line.partition("\t")[0]; 41*3fc6b790SMilton Miller files.append(fn) 42*3fc6b790SMilton Miller lines += 1 43*3fc6b790SMilton Miller finally: 44*3fc6b790SMilton Miller envcfg.close() 45*3fc6b790SMilton Miller if (lines < 1 or lines > 2 or (lines == 2 and files[0] != files[1])): 46*3fc6b790SMilton Miller raise Exception("Error parsing %s\n" % fw_env) 47*3fc6b790SMilton Miller shutil.copyfile(files[0], os.path.join(UPDATE_PATH, "image-u-boot-env")) 48*3fc6b790SMilton Miller 4940a360c2SBrad Bishopclass BmcFlashControl(DbusProperties, DbusObjectManager): 5040a360c2SBrad Bishop def __init__(self, bus, name): 51f47f5faaSBrad Bishop super(BmcFlashControl, self).__init__( 52f47f5faaSBrad Bishop conn=bus, 53f47f5faaSBrad Bishop object_path=name) 5440a360c2SBrad Bishop 5540a360c2SBrad Bishop self.Set(DBUS_NAME, "status", "Idle") 5640a360c2SBrad Bishop self.Set(DBUS_NAME, "filename", "") 57c8094109SMilton Miller self.Set(DBUS_NAME, "preserve_network_settings", True) 5840a360c2SBrad Bishop self.Set(DBUS_NAME, "restore_application_defaults", False) 5940a360c2SBrad Bishop self.Set(DBUS_NAME, "update_kernel_and_apps", False) 6040a360c2SBrad Bishop self.Set(DBUS_NAME, "clear_persistent_files", False) 610c8c5d4aSMilton Miller self.Set(DBUS_NAME, "auto_apply", False) 6240a360c2SBrad Bishop 6334c3bf9eSBrad Bishop bus.add_signal_receiver( 6434c3bf9eSBrad Bishop self.download_error_handler, signal_name="DownloadError") 6534c3bf9eSBrad Bishop bus.add_signal_receiver( 6634c3bf9eSBrad Bishop self.download_complete_handler, signal_name="DownloadComplete") 6740a360c2SBrad Bishop 680c8c5d4aSMilton Miller self.update_process = None 690c8c5d4aSMilton Miller self.progress_name = None 700c8c5d4aSMilton Miller 7134c3bf9eSBrad Bishop @dbus.service.method( 7234c3bf9eSBrad Bishop DBUS_NAME, in_signature='ss', out_signature='') 7340a360c2SBrad Bishop def updateViaTftp(self, ip, filename): 7440a360c2SBrad Bishop self.Set(DBUS_NAME, "status", "Downloading") 750c8c5d4aSMilton Miller self.TftpDownload(ip, filename) 7640a360c2SBrad Bishop 7734c3bf9eSBrad Bishop @dbus.service.method( 7834c3bf9eSBrad Bishop DBUS_NAME, in_signature='s', out_signature='') 7940a360c2SBrad Bishop def update(self, filename): 8040a360c2SBrad Bishop self.Set(DBUS_NAME, "filename", filename) 8140a360c2SBrad Bishop self.download_complete_handler(filename, filename) 8240a360c2SBrad Bishop 8340a360c2SBrad Bishop @dbus.service.signal(DOWNLOAD_INTF, signature='ss') 8440a360c2SBrad Bishop def TftpDownload(self, ip, filename): 8540a360c2SBrad Bishop self.Set(DBUS_NAME, "filename", filename) 8640a360c2SBrad Bishop pass 8740a360c2SBrad Bishop 8840a360c2SBrad Bishop ## Signal handler 8940a360c2SBrad Bishop def download_error_handler(self, filename): 9040a360c2SBrad Bishop if (filename == self.Get(DBUS_NAME, "filename")): 9140a360c2SBrad Bishop self.Set(DBUS_NAME, "status", "Download Error") 9240a360c2SBrad Bishop 9340a360c2SBrad Bishop def download_complete_handler(self, outfile, filename): 9440a360c2SBrad Bishop ## do update 9540a360c2SBrad Bishop if (filename != self.Get(DBUS_NAME, "filename")): 9640a360c2SBrad Bishop return 9740a360c2SBrad Bishop 9840a360c2SBrad Bishop print "Download complete. Updating..." 9940a360c2SBrad Bishop 10040a360c2SBrad Bishop self.Set(DBUS_NAME, "status", "Download Complete") 10140a360c2SBrad Bishop copy_files = {} 10240a360c2SBrad Bishop 10340a360c2SBrad Bishop ## determine needed files 10434c3bf9eSBrad Bishop if not self.Get(DBUS_NAME, "update_kernel_and_apps"): 10540a360c2SBrad Bishop copy_files["image-bmc"] = True 10640a360c2SBrad Bishop else: 10740a360c2SBrad Bishop copy_files["image-kernel"] = True 10840a360c2SBrad Bishop copy_files["image-rofs"] = True 10940a360c2SBrad Bishop 11034c3bf9eSBrad Bishop if self.Get(DBUS_NAME, "restore_application_defaults"): 11140a360c2SBrad Bishop copy_files["image-rwfs"] = True 11240a360c2SBrad Bishop 11340a360c2SBrad Bishop ## make sure files exist in archive 11440a360c2SBrad Bishop try: 11540a360c2SBrad Bishop tar = tarfile.open(outfile, "r") 11640a360c2SBrad Bishop files = {} 11740a360c2SBrad Bishop for f in tar.getnames(): 11840a360c2SBrad Bishop files[f] = True 11940a360c2SBrad Bishop tar.close() 12040a360c2SBrad Bishop for f in copy_files.keys(): 12134c3bf9eSBrad Bishop if f not in files: 12234c3bf9eSBrad Bishop raise Exception( 12334c3bf9eSBrad Bishop "ERROR: File not found in update archive: "+f) 12440a360c2SBrad Bishop 12540a360c2SBrad Bishop except Exception as e: 12640a360c2SBrad Bishop print e 1270c8c5d4aSMilton Miller self.Set(DBUS_NAME, "status", "Unpack Error") 12840a360c2SBrad Bishop return 12940a360c2SBrad Bishop 13040a360c2SBrad Bishop try: 13140a360c2SBrad Bishop tar = tarfile.open(outfile, "r") 13240a360c2SBrad Bishop tar.extractall(UPDATE_PATH, members=doExtract(tar, copy_files)) 13340a360c2SBrad Bishop tar.close() 13440a360c2SBrad Bishop 13534c3bf9eSBrad Bishop if self.Get(DBUS_NAME, "clear_persistent_files"): 13640a360c2SBrad Bishop print "Removing persistent files" 137b6cfc545SMilton Miller try: 13840a360c2SBrad Bishop os.unlink(UPDATE_PATH+"/whitelist") 139b6cfc545SMilton Miller except OSError as e: 140b6cfc545SMilton Miller if (e.errno == errno.EISDIR): 141b6cfc545SMilton Miller pass 142b6cfc545SMilton Miller elif (e.errno == errno.ENOENT): 143b6cfc545SMilton Miller pass 144b6cfc545SMilton Miller else: 145b6cfc545SMilton Miller raise 146b6cfc545SMilton Miller 147b6cfc545SMilton Miller try: 148b6cfc545SMilton Miller wldir = UPDATE_PATH + "/whitelist.d" 149b6cfc545SMilton Miller 150b6cfc545SMilton Miller for file in os.listdir(wldir): 151b6cfc545SMilton Miller os.unlink(os.path.join(wldir, file)) 152b6cfc545SMilton Miller except OSError as e: 153b6cfc545SMilton Miller if (e.errno == errno.EISDIR): 154b6cfc545SMilton Miller pass 155b6cfc545SMilton Miller else: 156b6cfc545SMilton Miller raise 157b6cfc545SMilton Miller 15834c3bf9eSBrad Bishop if self.Get(DBUS_NAME, "preserve_network_settings"): 15940a360c2SBrad Bishop print "Preserving network settings" 160*3fc6b790SMilton Miller save_fw_env() 16140a360c2SBrad Bishop 16240a360c2SBrad Bishop except Exception as e: 16340a360c2SBrad Bishop print e 1640c8c5d4aSMilton Miller self.Set(DBUS_NAME, "status", "Unpack Error") 1650c8c5d4aSMilton Miller 1660c8c5d4aSMilton Miller self.Verify() 1670c8c5d4aSMilton Miller 1680c8c5d4aSMilton Miller def Verify(self): 1690c8c5d4aSMilton Miller self.Set(DBUS_NAME, "status", "Checking Image") 1700c8c5d4aSMilton Miller try: 1710c8c5d4aSMilton Miller subprocess.check_call([ 1720c8c5d4aSMilton Miller "/run/initramfs/update", 1730c8c5d4aSMilton Miller "--no-flash", 1740c8c5d4aSMilton Miller "--no-save-files", 1750c8c5d4aSMilton Miller "--no-restore-files", 1760c8c5d4aSMilton Miller "--no-clean-saved-files"]) 1770c8c5d4aSMilton Miller 1780c8c5d4aSMilton Miller self.Set(DBUS_NAME, "status", "Image ready to apply.") 1790c8c5d4aSMilton Miller if (self.Get(DBUS_NAME, "auto_apply")): 1800c8c5d4aSMilton Miller self.Apply() 1810c8c5d4aSMilton Miller except: 1820c8c5d4aSMilton Miller self.Set(DBUS_NAME, "auto_apply", False) 1830c8c5d4aSMilton Miller try: 1840c8c5d4aSMilton Miller subprocess.check_output([ 1850c8c5d4aSMilton Miller "/run/initramfs/update", 1860c8c5d4aSMilton Miller "--no-flash", 1870c8c5d4aSMilton Miller "--ignore-mount", 1880c8c5d4aSMilton Miller "--no-save-files", 1890c8c5d4aSMilton Miller "--no-restore-files", 1900c8c5d4aSMilton Miller "--no-clean-saved-files"], 1910c8c5d4aSMilton Miller stderr=subprocess.STDOUT) 19234c3bf9eSBrad Bishop self.Set( 19334c3bf9eSBrad Bishop DBUS_NAME, "status", 19434c3bf9eSBrad Bishop "Deferred for mounted filesystem. reboot BMC to apply.") 1950c8c5d4aSMilton Miller except subprocess.CalledProcessError as e: 19634c3bf9eSBrad Bishop self.Set( 19734c3bf9eSBrad Bishop DBUS_NAME, "status", "Verify error: %s" % e.output) 1980c8c5d4aSMilton Miller except OSError as e: 19934c3bf9eSBrad Bishop self.Set( 20034c3bf9eSBrad Bishop DBUS_NAME, "status", 20134c3bf9eSBrad Bishop "Verify error: problem calling update: %s" % e.strerror) 20240a360c2SBrad Bishop 2030c8c5d4aSMilton Miller def Cleanup(self): 2040c8c5d4aSMilton Miller if self.progress_name: 2050c8c5d4aSMilton Miller try: 2060c8c5d4aSMilton Miller os.unlink(self.progress_name) 2070c8c5d4aSMilton Miller self.progress_name = None 2080c8c5d4aSMilton Miller except oserror as e: 2090c8c5d4aSMilton Miller if e.errno == EEXIST: 2100c8c5d4aSMilton Miller pass 2110c8c5d4aSMilton Miller raise 2120c8c5d4aSMilton Miller self.update_process = None 2130c8c5d4aSMilton Miller self.Set(DBUS_NAME, "status", "Idle") 21440a360c2SBrad Bishop 21534c3bf9eSBrad Bishop @dbus.service.method( 21634c3bf9eSBrad Bishop DBUS_NAME, in_signature='', out_signature='') 2170c8c5d4aSMilton Miller def Abort(self): 2180c8c5d4aSMilton Miller if self.update_process: 2190c8c5d4aSMilton Miller try: 2200c8c5d4aSMilton Miller self.update_process.kill() 2210c8c5d4aSMilton Miller except: 2220c8c5d4aSMilton Miller pass 2230c8c5d4aSMilton Miller for file in os.listdir(UPDATE_PATH): 2240c8c5d4aSMilton Miller if file.startswith('image-'): 2250c8c5d4aSMilton Miller os.unlink(os.path.join(UPDATE_PATH, file)) 2260c8c5d4aSMilton Miller 22734c3bf9eSBrad Bishop self.Cleanup() 2280c8c5d4aSMilton Miller 22934c3bf9eSBrad Bishop @dbus.service.method( 23034c3bf9eSBrad Bishop DBUS_NAME, in_signature='', out_signature='s') 2310c8c5d4aSMilton Miller def GetUpdateProgress(self): 2320c8c5d4aSMilton Miller msg = "" 2330c8c5d4aSMilton Miller 2340c8c5d4aSMilton Miller if self.update_process and self.update_process.returncode is None: 2350c8c5d4aSMilton Miller self.update_process.poll() 2360c8c5d4aSMilton Miller 2370c8c5d4aSMilton Miller if (self.update_process is None): 2380c8c5d4aSMilton Miller pass 2390c8c5d4aSMilton Miller elif (self.update_process.returncode > 0): 2400c8c5d4aSMilton Miller self.Set(DBUS_NAME, "status", "Apply failed") 2410c8c5d4aSMilton Miller elif (self.update_process.returncode is None): 2420c8c5d4aSMilton Miller pass 2430c8c5d4aSMilton Miller else: # (self.update_process.returncode == 0) 2440c8c5d4aSMilton Miller files = "" 2450c8c5d4aSMilton Miller for file in os.listdir(UPDATE_PATH): 2460c8c5d4aSMilton Miller if file.startswith('image-'): 24734c3bf9eSBrad Bishop files = files + file 2480c8c5d4aSMilton Miller if files == "": 2490c8c5d4aSMilton Miller msg = "Apply Complete. Reboot to take effect." 2500c8c5d4aSMilton Miller else: 2510c8c5d4aSMilton Miller msg = "Apply Incomplete, Remaining:" + files 2520c8c5d4aSMilton Miller self.Set(DBUS_NAME, "status", msg) 2530c8c5d4aSMilton Miller 25434c3bf9eSBrad Bishop msg = self.Get(DBUS_NAME, "status") + "\n" 2550c8c5d4aSMilton Miller if self.progress_name: 2560c8c5d4aSMilton Miller try: 2570c8c5d4aSMilton Miller prog = open(self.progress_name, 'r') 2580c8c5d4aSMilton Miller for line in prog: 2590c8c5d4aSMilton Miller # strip off initial sets of xxx\r here 2600c8c5d4aSMilton Miller # ignore crlf at the end 2610c8c5d4aSMilton Miller # cr will be -1 if no '\r' is found 2620c8c5d4aSMilton Miller cr = line.rfind("\r", 0, -2) 2630c8c5d4aSMilton Miller msg = msg + line[cr + 1:] 2640c8c5d4aSMilton Miller except OSError as e: 2650c8c5d4aSMilton Miller if (e.error == EEXIST): 2660c8c5d4aSMilton Miller pass 2670c8c5d4aSMilton Miller raise 2680c8c5d4aSMilton Miller return msg 2690c8c5d4aSMilton Miller 27034c3bf9eSBrad Bishop @dbus.service.method( 27134c3bf9eSBrad Bishop DBUS_NAME, in_signature='', out_signature='') 2720c8c5d4aSMilton Miller def Apply(self): 2730c8c5d4aSMilton Miller progress = None 2740c8c5d4aSMilton Miller self.Set(DBUS_NAME, "status", "Writing images to flash") 2750c8c5d4aSMilton Miller try: 2760c8c5d4aSMilton Miller progress = tempfile.NamedTemporaryFile( 2770c8c5d4aSMilton Miller delete=False, prefix="progress.") 2780c8c5d4aSMilton Miller self.progress_name = progress.name 2790c8c5d4aSMilton Miller self.update_process = subprocess.Popen([ 2800c8c5d4aSMilton Miller "/run/initramfs/update"], 2810c8c5d4aSMilton Miller stdout=progress.file, 2820c8c5d4aSMilton Miller stderr=subprocess.STDOUT) 2830c8c5d4aSMilton Miller except Exception as e: 2840c8c5d4aSMilton Miller try: 2850c8c5d4aSMilton Miller progress.close() 2860c8c5d4aSMilton Miller os.unlink(progress.name) 2870c8c5d4aSMilton Miller self.progress_name = None 2880c8c5d4aSMilton Miller except: 2890c8c5d4aSMilton Miller pass 2900c8c5d4aSMilton Miller raise 2910c8c5d4aSMilton Miller 2920c8c5d4aSMilton Miller try: 2930c8c5d4aSMilton Miller progress.close() 2940c8c5d4aSMilton Miller except: 2950c8c5d4aSMilton Miller pass 2960c8c5d4aSMilton Miller 29734c3bf9eSBrad Bishop @dbus.service.method( 29834c3bf9eSBrad Bishop DBUS_NAME, in_signature='', out_signature='') 2990c8c5d4aSMilton Miller def PrepareForUpdate(self): 3000c8c5d4aSMilton Miller subprocess.call([ 3010c8c5d4aSMilton Miller "fw_setenv", 3020c8c5d4aSMilton Miller "openbmconce", 3030c8c5d4aSMilton Miller "copy-files-to-ram copy-base-filesystem-to-ram"]) 3040c8c5d4aSMilton Miller self.Set(DBUS_NAME, "status", "Switch to update mode in progress") 3050c8c5d4aSMilton Miller o = bus.get_object(BMC_DBUS_NAME, BMC_OBJ_NAME) 3060c8c5d4aSMilton Miller intf = dbus.Interface(o, BMC_DBUS_NAME) 3070c8c5d4aSMilton Miller intf.warmReset() 30840a360c2SBrad Bishop 30940a360c2SBrad Bishop 31040a360c2SBrad Bishopif __name__ == '__main__': 31140a360c2SBrad Bishop dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) 31240a360c2SBrad Bishop 31340a360c2SBrad Bishop bus = get_dbus() 31440a360c2SBrad Bishop obj = BmcFlashControl(bus, OBJ_NAME) 31540a360c2SBrad Bishop mainloop = gobject.MainLoop() 316f0f3efe1SBrad Bishop 317f0f3efe1SBrad Bishop obj.unmask_signals() 31870852a38SBrad Bishop name = dbus.service.BusName(DBUS_NAME, bus) 31940a360c2SBrad Bishop 32040a360c2SBrad Bishop print "Running Bmc Flash Control" 32140a360c2SBrad Bishop mainloop.run() 32253066750SBrad Bishop 32353066750SBrad Bishop# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 324