blob: e761fffd7c4287117ac888eb5d9ec7c4d18cb454 [file] [log] [blame]
Brad Bishop34c3bf92016-08-30 20:34:51 -04001#!/usr/bin/env python
Norman James3d6a06f2016-01-30 13:03:45 -06002
3import gobject
4import dbus
5import dbus.service
6import dbus.mainloop.glib
Milton Miller0c8c5d42016-06-29 17:41:36 -05007import subprocess
8import tempfile
Norman James3d6a06f2016-01-30 13:03:45 -06009import shutil
10import tarfile
Norman James8a65a542016-02-03 19:04:44 -060011import os
Brad Bishop84e73b52016-05-12 15:57:52 -040012from obmc.dbuslib.bindings import get_dbus, DbusProperties, DbusObjectManager
Norman James3d6a06f2016-01-30 13:03:45 -060013
14DBUS_NAME = 'org.openbmc.control.BmcFlash'
15OBJ_NAME = '/org/openbmc/control/flash/bmc'
Norman James3d6a06f2016-01-30 13:03:45 -060016DOWNLOAD_INTF = 'org.openbmc.managers.Download'
17
Milton Miller0c8c5d42016-06-29 17:41:36 -050018BMC_DBUS_NAME = 'org.openbmc.control.Bmc'
19BMC_OBJ_NAME = '/org/openbmc/control/bmc0'
20
Norman James3d6a06f2016-01-30 13:03:45 -060021UPDATE_PATH = '/run/initramfs'
22
23
Brad Bishop34c3bf92016-08-30 20:34:51 -040024def doExtract(members, files):
Norman James3d6a06f2016-01-30 13:03:45 -060025 for tarinfo in members:
Brad Bishop34c3bf92016-08-30 20:34:51 -040026 if tarinfo.name in files:
Norman James3d6a06f2016-01-30 13:03:45 -060027 yield tarinfo
28
29
Brad Bishop34c3bf92016-08-30 20:34:51 -040030class BmcFlashControl(DbusProperties, DbusObjectManager):
31 def __init__(self, bus, name):
32 self.dbus_objects = {}
33 DbusProperties.__init__(self)
34 DbusObjectManager.__init__(self)
35 dbus.service.Object.__init__(self, bus, name)
Milton Miller0c8c5d42016-06-29 17:41:36 -050036
Brad Bishop34c3bf92016-08-30 20:34:51 -040037 self.Set(DBUS_NAME, "status", "Idle")
38 self.Set(DBUS_NAME, "filename", "")
39 self.Set(DBUS_NAME, "preserve_network_settings", True)
40 self.Set(DBUS_NAME, "restore_application_defaults", False)
41 self.Set(DBUS_NAME, "update_kernel_and_apps", False)
42 self.Set(DBUS_NAME, "clear_persistent_files", False)
43 self.Set(DBUS_NAME, "auto_apply", False)
Norman James3d6a06f2016-01-30 13:03:45 -060044
Brad Bishop34c3bf92016-08-30 20:34:51 -040045 bus.add_signal_receiver(
46 self.download_error_handler, signal_name="DownloadError")
47 bus.add_signal_receiver(
48 self.download_complete_handler, signal_name="DownloadComplete")
Milton Miller0c8c5d42016-06-29 17:41:36 -050049
Brad Bishop34c3bf92016-08-30 20:34:51 -040050 self.update_process = None
51 self.progress_name = None
Norman James3d6a06f2016-01-30 13:03:45 -060052
Brad Bishop34c3bf92016-08-30 20:34:51 -040053 @dbus.service.method(
54 DBUS_NAME, in_signature='ss', out_signature='')
55 def updateViaTftp(self, ip, filename):
56 self.Set(DBUS_NAME, "status", "Downloading")
57 self.TftpDownload(ip, filename)
Milton Miller0c8c5d42016-06-29 17:41:36 -050058
Brad Bishop34c3bf92016-08-30 20:34:51 -040059 @dbus.service.method(
60 DBUS_NAME, in_signature='s', out_signature='')
61 def update(self, filename):
62 self.Set(DBUS_NAME, "filename", filename)
63 self.download_complete_handler(filename, filename)
Chris Austenea5b4012016-05-20 16:41:40 -050064
Brad Bishop34c3bf92016-08-30 20:34:51 -040065 @dbus.service.signal(DOWNLOAD_INTF, signature='ss')
66 def TftpDownload(self, ip, filename):
67 self.Set(DBUS_NAME, "filename", filename)
68 pass
Norman James3d6a06f2016-01-30 13:03:45 -060069
Brad Bishop34c3bf92016-08-30 20:34:51 -040070 ## Signal handler
71 def download_error_handler(self, filename):
72 if (filename == self.Get(DBUS_NAME, "filename")):
73 self.Set(DBUS_NAME, "status", "Download Error")
Norman James3d6a06f2016-01-30 13:03:45 -060074
Brad Bishop34c3bf92016-08-30 20:34:51 -040075 def download_complete_handler(self, outfile, filename):
76 ## do update
77 if (filename != self.Get(DBUS_NAME, "filename")):
78 return
Norman James3d6a06f2016-01-30 13:03:45 -060079
Brad Bishop34c3bf92016-08-30 20:34:51 -040080 print "Download complete. Updating..."
Norman James3d6a06f2016-01-30 13:03:45 -060081
Brad Bishop34c3bf92016-08-30 20:34:51 -040082 self.Set(DBUS_NAME, "status", "Download Complete")
83 copy_files = {}
Norman James3d6a06f2016-01-30 13:03:45 -060084
Brad Bishop34c3bf92016-08-30 20:34:51 -040085 ## determine needed files
86 if not self.Get(DBUS_NAME, "update_kernel_and_apps"):
87 copy_files["image-bmc"] = True
88 else:
89 copy_files["image-kernel"] = True
90 copy_files["image-initramfs"] = True
91 copy_files["image-rofs"] = True
Norman James3d6a06f2016-01-30 13:03:45 -060092
Brad Bishop34c3bf92016-08-30 20:34:51 -040093 if self.Get(DBUS_NAME, "restore_application_defaults"):
94 copy_files["image-rwfs"] = True
Norman James8a65a542016-02-03 19:04:44 -060095
Brad Bishop34c3bf92016-08-30 20:34:51 -040096 ## make sure files exist in archive
97 try:
98 tar = tarfile.open(outfile, "r")
99 files = {}
100 for f in tar.getnames():
101 files[f] = True
102 tar.close()
103 for f in copy_files.keys():
104 if f not in files:
105 raise Exception(
106 "ERROR: File not found in update archive: "+f)
Milton Millerb6cfc542016-06-17 11:43:40 -0500107
Brad Bishop34c3bf92016-08-30 20:34:51 -0400108 except Exception as e:
109 print e
110 self.Set(DBUS_NAME, "status", "Unpack Error")
111 return
Milton Millerb6cfc542016-06-17 11:43:40 -0500112
Brad Bishop34c3bf92016-08-30 20:34:51 -0400113 try:
114 tar = tarfile.open(outfile, "r")
115 tar.extractall(UPDATE_PATH, members=doExtract(tar, copy_files))
116 tar.close()
Milton Millerb6cfc542016-06-17 11:43:40 -0500117
Brad Bishop34c3bf92016-08-30 20:34:51 -0400118 if self.Get(DBUS_NAME, "clear_persistent_files"):
119 print "Removing persistent files"
120 try:
121 os.unlink(UPDATE_PATH+"/whitelist")
122 except OSError as e:
123 if (e.errno == errno.EISDIR):
124 pass
125 elif (e.errno == errno.ENOENT):
126 pass
127 else:
128 raise
Norman James3d6a06f2016-01-30 13:03:45 -0600129
Brad Bishop34c3bf92016-08-30 20:34:51 -0400130 try:
131 wldir = UPDATE_PATH + "/whitelist.d"
Milton Miller0c8c5d42016-06-29 17:41:36 -0500132
Brad Bishop34c3bf92016-08-30 20:34:51 -0400133 for file in os.listdir(wldir):
134 os.unlink(os.path.join(wldir, file))
135 except OSError as e:
136 if (e.errno == errno.EISDIR):
137 pass
138 else:
139 raise
Milton Miller0c8c5d42016-06-29 17:41:36 -0500140
Brad Bishop34c3bf92016-08-30 20:34:51 -0400141 if self.Get(DBUS_NAME, "preserve_network_settings"):
142 print "Preserving network settings"
143 shutil.copy2("/run/fw_env", UPDATE_PATH+"/image-u-boot-env")
Milton Miller0c8c5d42016-06-29 17:41:36 -0500144
Brad Bishop34c3bf92016-08-30 20:34:51 -0400145 except Exception as e:
146 print e
147 self.Set(DBUS_NAME, "status", "Unpack Error")
Milton Miller0c8c5d42016-06-29 17:41:36 -0500148
Brad Bishop34c3bf92016-08-30 20:34:51 -0400149 self.Verify()
Milton Miller0c8c5d42016-06-29 17:41:36 -0500150
Brad Bishop34c3bf92016-08-30 20:34:51 -0400151 def Verify(self):
152 self.Set(DBUS_NAME, "status", "Checking Image")
153 try:
154 subprocess.check_call([
155 "/run/initramfs/update",
156 "--no-flash",
157 "--no-save-files",
158 "--no-restore-files",
159 "--no-clean-saved-files"])
Milton Miller0c8c5d42016-06-29 17:41:36 -0500160
Brad Bishop34c3bf92016-08-30 20:34:51 -0400161 self.Set(DBUS_NAME, "status", "Image ready to apply.")
162 if (self.Get(DBUS_NAME, "auto_apply")):
163 self.Apply()
164 except:
165 self.Set(DBUS_NAME, "auto_apply", False)
166 try:
167 subprocess.check_output([
168 "/run/initramfs/update",
169 "--no-flash",
170 "--ignore-mount",
171 "--no-save-files",
172 "--no-restore-files",
173 "--no-clean-saved-files"],
174 stderr=subprocess.STDOUT)
175 self.Set(
176 DBUS_NAME, "status",
177 "Deferred for mounted filesystem. reboot BMC to apply.")
178 except subprocess.CalledProcessError as e:
179 self.Set(
180 DBUS_NAME, "status", "Verify error: %s" % e.output)
181 except OSError as e:
182 self.Set(
183 DBUS_NAME, "status",
184 "Verify error: problem calling update: %s" % e.strerror)
Milton Miller0c8c5d42016-06-29 17:41:36 -0500185
Brad Bishop34c3bf92016-08-30 20:34:51 -0400186 def Cleanup(self):
187 if self.progress_name:
188 try:
189 os.unlink(self.progress_name)
190 self.progress_name = None
191 except oserror as e:
192 if e.errno == EEXIST:
193 pass
194 raise
195 self.update_process = None
196 self.Set(DBUS_NAME, "status", "Idle")
Milton Miller0c8c5d42016-06-29 17:41:36 -0500197
Brad Bishop34c3bf92016-08-30 20:34:51 -0400198 @dbus.service.method(
199 DBUS_NAME, in_signature='', out_signature='')
200 def Abort(self):
201 if self.update_process:
202 try:
203 self.update_process.kill()
204 except:
205 pass
206 for file in os.listdir(UPDATE_PATH):
207 if file.startswith('image-'):
208 os.unlink(os.path.join(UPDATE_PATH, file))
Milton Miller0c8c5d42016-06-29 17:41:36 -0500209
Brad Bishop34c3bf92016-08-30 20:34:51 -0400210 self.Cleanup()
Milton Miller0c8c5d42016-06-29 17:41:36 -0500211
Brad Bishop34c3bf92016-08-30 20:34:51 -0400212 @dbus.service.method(
213 DBUS_NAME, in_signature='', out_signature='s')
214 def GetUpdateProgress(self):
215 msg = ""
Milton Miller0c8c5d42016-06-29 17:41:36 -0500216
Brad Bishop34c3bf92016-08-30 20:34:51 -0400217 if self.update_process and self.update_process.returncode is None:
218 self.update_process.poll()
Milton Miller0c8c5d42016-06-29 17:41:36 -0500219
Brad Bishop34c3bf92016-08-30 20:34:51 -0400220 if (self.update_process is None):
221 pass
222 elif (self.update_process.returncode > 0):
223 self.Set(DBUS_NAME, "status", "Apply failed")
224 elif (self.update_process.returncode is None):
225 pass
226 else: # (self.update_process.returncode == 0)
227 files = ""
228 for file in os.listdir(UPDATE_PATH):
229 if file.startswith('image-'):
230 files = files + file
231 if files == "":
232 msg = "Apply Complete. Reboot to take effect."
233 else:
234 msg = "Apply Incomplete, Remaining:" + files
235 self.Set(DBUS_NAME, "status", msg)
Milton Miller0c8c5d42016-06-29 17:41:36 -0500236
Brad Bishop34c3bf92016-08-30 20:34:51 -0400237 msg = self.Get(DBUS_NAME, "status") + "\n"
238 if self.progress_name:
239 try:
240 prog = open(self.progress_name, 'r')
241 for line in prog:
242 # strip off initial sets of xxx\r here
243 # ignore crlf at the end
244 # cr will be -1 if no '\r' is found
245 cr = line.rfind("\r", 0, -2)
246 msg = msg + line[cr + 1:]
247 except OSError as e:
248 if (e.error == EEXIST):
249 pass
250 raise
251 return msg
252
253 @dbus.service.method(
254 DBUS_NAME, in_signature='', out_signature='')
255 def Apply(self):
256 progress = None
257 self.Set(DBUS_NAME, "status", "Writing images to flash")
258 try:
259 progress = tempfile.NamedTemporaryFile(
260 delete=False, prefix="progress.")
261 self.progress_name = progress.name
262 self.update_process = subprocess.Popen([
263 "/run/initramfs/update"],
264 stdout=progress.file,
265 stderr=subprocess.STDOUT)
266 except Exception as e:
267 try:
268 progress.close()
269 os.unlink(progress.name)
270 self.progress_name = None
271 except:
272 pass
273 raise
274
275 try:
276 progress.close()
277 except:
278 pass
279
280 @dbus.service.method(
281 DBUS_NAME, in_signature='', out_signature='')
282 def PrepareForUpdate(self):
283 subprocess.call([
284 "fw_setenv",
285 "openbmconce",
286 "copy-files-to-ram copy-base-filesystem-to-ram"])
287 self.Set(DBUS_NAME, "status", "Switch to update mode in progress")
288 o = bus.get_object(BMC_DBUS_NAME, BMC_OBJ_NAME)
289 intf = dbus.Interface(o, BMC_DBUS_NAME)
290 intf.warmReset()
Milton Miller0c8c5d42016-06-29 17:41:36 -0500291
Norman James3d6a06f2016-01-30 13:03:45 -0600292
293if __name__ == '__main__':
294 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
295
Brad Bishop84e73b52016-05-12 15:57:52 -0400296 bus = get_dbus()
Norman James3d6a06f2016-01-30 13:03:45 -0600297 obj = BmcFlashControl(bus, OBJ_NAME)
298 mainloop = gobject.MainLoop()
Brad Bishopf0f3efe2016-06-29 23:20:24 -0400299
300 obj.unmask_signals()
Brad Bishop70852a32016-06-29 22:58:51 -0400301 name = dbus.service.BusName(DBUS_NAME, bus)
Brad Bishop34c3bf92016-08-30 20:34:51 -0400302
Norman James3d6a06f2016-01-30 13:03:45 -0600303 print "Running Bmc Flash Control"
304 mainloop.run()