blob: c934e1a37278c7424c28ff31238416c569a09f40 [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
Milton Miller3fc6b792017-08-15 06:57:14 +000030def save_fw_env():
31 fw_env = "/etc/fw_env.config"
32 lines = 0
Adriana Kobylak24341f92018-01-26 15:07:23 -060033 files = []
Milton Miller3fc6b792017-08-15 06:57:14 +000034 envcfg = open(fw_env, 'r')
35 try:
36 for line in envcfg.readlines():
37 # ignore lines that are blank or start with #
Adriana Kobylak24341f92018-01-26 15:07:23 -060038 if (line.startswith("#")):
39 continue
40 if (not len(line.strip())):
41 continue
42 fn = line.partition("\t")[0]
Milton Miller3fc6b792017-08-15 06:57:14 +000043 files.append(fn)
44 lines += 1
45 finally:
46 envcfg.close()
47 if (lines < 1 or lines > 2 or (lines == 2 and files[0] != files[1])):
48 raise Exception("Error parsing %s\n" % fw_env)
49 shutil.copyfile(files[0], os.path.join(UPDATE_PATH, "image-u-boot-env"))
50
Adriana Kobylak24341f92018-01-26 15:07:23 -060051
Brad Bishop34c3bf92016-08-30 20:34:51 -040052class BmcFlashControl(DbusProperties, DbusObjectManager):
53 def __init__(self, bus, name):
Brad Bishopf47f5fa2016-09-08 22:29:01 -040054 super(BmcFlashControl, self).__init__(
55 conn=bus,
56 object_path=name)
Milton Miller0c8c5d42016-06-29 17:41:36 -050057
Brad Bishop34c3bf92016-08-30 20:34:51 -040058 self.Set(DBUS_NAME, "status", "Idle")
59 self.Set(DBUS_NAME, "filename", "")
60 self.Set(DBUS_NAME, "preserve_network_settings", True)
61 self.Set(DBUS_NAME, "restore_application_defaults", False)
62 self.Set(DBUS_NAME, "update_kernel_and_apps", False)
63 self.Set(DBUS_NAME, "clear_persistent_files", False)
64 self.Set(DBUS_NAME, "auto_apply", False)
Norman James3d6a06f2016-01-30 13:03:45 -060065
Brad Bishop34c3bf92016-08-30 20:34:51 -040066 bus.add_signal_receiver(
67 self.download_error_handler, signal_name="DownloadError")
68 bus.add_signal_receiver(
69 self.download_complete_handler, signal_name="DownloadComplete")
Milton Miller0c8c5d42016-06-29 17:41:36 -050070
Brad Bishop34c3bf92016-08-30 20:34:51 -040071 self.update_process = None
72 self.progress_name = None
Norman James3d6a06f2016-01-30 13:03:45 -060073
Brad Bishop34c3bf92016-08-30 20:34:51 -040074 @dbus.service.method(
75 DBUS_NAME, in_signature='ss', out_signature='')
76 def updateViaTftp(self, ip, filename):
77 self.Set(DBUS_NAME, "status", "Downloading")
78 self.TftpDownload(ip, filename)
Milton Miller0c8c5d42016-06-29 17:41:36 -050079
Brad Bishop34c3bf92016-08-30 20:34:51 -040080 @dbus.service.method(
81 DBUS_NAME, in_signature='s', out_signature='')
82 def update(self, filename):
83 self.Set(DBUS_NAME, "filename", filename)
84 self.download_complete_handler(filename, filename)
Chris Austenea5b4012016-05-20 16:41:40 -050085
Brad Bishop34c3bf92016-08-30 20:34:51 -040086 @dbus.service.signal(DOWNLOAD_INTF, signature='ss')
87 def TftpDownload(self, ip, filename):
88 self.Set(DBUS_NAME, "filename", filename)
89 pass
Norman James3d6a06f2016-01-30 13:03:45 -060090
Adriana Kobylak24341f92018-01-26 15:07:23 -060091 # Signal handler
Brad Bishop34c3bf92016-08-30 20:34:51 -040092 def download_error_handler(self, filename):
93 if (filename == self.Get(DBUS_NAME, "filename")):
94 self.Set(DBUS_NAME, "status", "Download Error")
Norman James3d6a06f2016-01-30 13:03:45 -060095
Brad Bishop34c3bf92016-08-30 20:34:51 -040096 def download_complete_handler(self, outfile, filename):
Adriana Kobylak24341f92018-01-26 15:07:23 -060097 # do update
Brad Bishop34c3bf92016-08-30 20:34:51 -040098 if (filename != self.Get(DBUS_NAME, "filename")):
99 return
Norman James3d6a06f2016-01-30 13:03:45 -0600100
Brad Bishop34c3bf92016-08-30 20:34:51 -0400101 print "Download complete. Updating..."
Norman James3d6a06f2016-01-30 13:03:45 -0600102
Brad Bishop34c3bf92016-08-30 20:34:51 -0400103 self.Set(DBUS_NAME, "status", "Download Complete")
104 copy_files = {}
Norman James3d6a06f2016-01-30 13:03:45 -0600105
Adriana Kobylak24341f92018-01-26 15:07:23 -0600106 # determine needed files
Brad Bishop34c3bf92016-08-30 20:34:51 -0400107 if not self.Get(DBUS_NAME, "update_kernel_and_apps"):
108 copy_files["image-bmc"] = True
109 else:
110 copy_files["image-kernel"] = True
Brad Bishop34c3bf92016-08-30 20:34:51 -0400111 copy_files["image-rofs"] = True
Norman James3d6a06f2016-01-30 13:03:45 -0600112
Brad Bishop34c3bf92016-08-30 20:34:51 -0400113 if self.Get(DBUS_NAME, "restore_application_defaults"):
114 copy_files["image-rwfs"] = True
Norman James8a65a542016-02-03 19:04:44 -0600115
Adriana Kobylak24341f92018-01-26 15:07:23 -0600116 # make sure files exist in archive
Brad Bishop34c3bf92016-08-30 20:34:51 -0400117 try:
118 tar = tarfile.open(outfile, "r")
119 files = {}
120 for f in tar.getnames():
121 files[f] = True
122 tar.close()
123 for f in copy_files.keys():
124 if f not in files:
125 raise Exception(
126 "ERROR: File not found in update archive: "+f)
Milton Millerb6cfc542016-06-17 11:43:40 -0500127
Brad Bishop34c3bf92016-08-30 20:34:51 -0400128 except Exception as e:
129 print e
130 self.Set(DBUS_NAME, "status", "Unpack Error")
131 return
Milton Millerb6cfc542016-06-17 11:43:40 -0500132
Brad Bishop34c3bf92016-08-30 20:34:51 -0400133 try:
134 tar = tarfile.open(outfile, "r")
135 tar.extractall(UPDATE_PATH, members=doExtract(tar, copy_files))
136 tar.close()
Milton Millerb6cfc542016-06-17 11:43:40 -0500137
Brad Bishop34c3bf92016-08-30 20:34:51 -0400138 if self.Get(DBUS_NAME, "clear_persistent_files"):
139 print "Removing persistent files"
140 try:
141 os.unlink(UPDATE_PATH+"/whitelist")
142 except OSError as e:
143 if (e.errno == errno.EISDIR):
144 pass
145 elif (e.errno == errno.ENOENT):
146 pass
147 else:
148 raise
Norman James3d6a06f2016-01-30 13:03:45 -0600149
Brad Bishop34c3bf92016-08-30 20:34:51 -0400150 try:
151 wldir = UPDATE_PATH + "/whitelist.d"
Milton Miller0c8c5d42016-06-29 17:41:36 -0500152
Brad Bishop34c3bf92016-08-30 20:34:51 -0400153 for file in os.listdir(wldir):
154 os.unlink(os.path.join(wldir, file))
155 except OSError as e:
156 if (e.errno == errno.EISDIR):
157 pass
158 else:
159 raise
Milton Miller0c8c5d42016-06-29 17:41:36 -0500160
Brad Bishop34c3bf92016-08-30 20:34:51 -0400161 if self.Get(DBUS_NAME, "preserve_network_settings"):
162 print "Preserving network settings"
Milton Miller3fc6b792017-08-15 06:57:14 +0000163 save_fw_env()
Milton Miller0c8c5d42016-06-29 17:41:36 -0500164
Brad Bishop34c3bf92016-08-30 20:34:51 -0400165 except Exception as e:
166 print e
167 self.Set(DBUS_NAME, "status", "Unpack Error")
Milton Miller0c8c5d42016-06-29 17:41:36 -0500168
Brad Bishop34c3bf92016-08-30 20:34:51 -0400169 self.Verify()
Milton Miller0c8c5d42016-06-29 17:41:36 -0500170
Brad Bishop34c3bf92016-08-30 20:34:51 -0400171 def Verify(self):
172 self.Set(DBUS_NAME, "status", "Checking Image")
173 try:
174 subprocess.check_call([
175 "/run/initramfs/update",
176 "--no-flash",
177 "--no-save-files",
178 "--no-restore-files",
179 "--no-clean-saved-files"])
Milton Miller0c8c5d42016-06-29 17:41:36 -0500180
Brad Bishop34c3bf92016-08-30 20:34:51 -0400181 self.Set(DBUS_NAME, "status", "Image ready to apply.")
182 if (self.Get(DBUS_NAME, "auto_apply")):
183 self.Apply()
Adriana Kobylak24341f92018-01-26 15:07:23 -0600184 except Exception:
Brad Bishop34c3bf92016-08-30 20:34:51 -0400185 self.Set(DBUS_NAME, "auto_apply", False)
186 try:
187 subprocess.check_output([
188 "/run/initramfs/update",
189 "--no-flash",
190 "--ignore-mount",
191 "--no-save-files",
192 "--no-restore-files",
193 "--no-clean-saved-files"],
194 stderr=subprocess.STDOUT)
195 self.Set(
196 DBUS_NAME, "status",
197 "Deferred for mounted filesystem. reboot BMC to apply.")
198 except subprocess.CalledProcessError as e:
199 self.Set(
200 DBUS_NAME, "status", "Verify error: %s" % e.output)
201 except OSError as e:
202 self.Set(
203 DBUS_NAME, "status",
204 "Verify error: problem calling update: %s" % e.strerror)
Milton Miller0c8c5d42016-06-29 17:41:36 -0500205
Brad Bishop34c3bf92016-08-30 20:34:51 -0400206 def Cleanup(self):
207 if self.progress_name:
208 try:
209 os.unlink(self.progress_name)
210 self.progress_name = None
211 except oserror as e:
212 if e.errno == EEXIST:
213 pass
214 raise
215 self.update_process = None
216 self.Set(DBUS_NAME, "status", "Idle")
Milton Miller0c8c5d42016-06-29 17:41:36 -0500217
Brad Bishop34c3bf92016-08-30 20:34:51 -0400218 @dbus.service.method(
219 DBUS_NAME, in_signature='', out_signature='')
220 def Abort(self):
221 if self.update_process:
222 try:
223 self.update_process.kill()
Adriana Kobylak24341f92018-01-26 15:07:23 -0600224 except Exception:
Brad Bishop34c3bf92016-08-30 20:34:51 -0400225 pass
226 for file in os.listdir(UPDATE_PATH):
227 if file.startswith('image-'):
228 os.unlink(os.path.join(UPDATE_PATH, file))
Milton Miller0c8c5d42016-06-29 17:41:36 -0500229
Brad Bishop34c3bf92016-08-30 20:34:51 -0400230 self.Cleanup()
Milton Miller0c8c5d42016-06-29 17:41:36 -0500231
Brad Bishop34c3bf92016-08-30 20:34:51 -0400232 @dbus.service.method(
233 DBUS_NAME, in_signature='', out_signature='s')
234 def GetUpdateProgress(self):
235 msg = ""
Milton Miller0c8c5d42016-06-29 17:41:36 -0500236
Brad Bishop34c3bf92016-08-30 20:34:51 -0400237 if self.update_process and self.update_process.returncode is None:
238 self.update_process.poll()
Milton Miller0c8c5d42016-06-29 17:41:36 -0500239
Brad Bishop34c3bf92016-08-30 20:34:51 -0400240 if (self.update_process is None):
241 pass
242 elif (self.update_process.returncode > 0):
243 self.Set(DBUS_NAME, "status", "Apply failed")
244 elif (self.update_process.returncode is None):
245 pass
246 else: # (self.update_process.returncode == 0)
247 files = ""
248 for file in os.listdir(UPDATE_PATH):
249 if file.startswith('image-'):
250 files = files + file
251 if files == "":
252 msg = "Apply Complete. Reboot to take effect."
253 else:
254 msg = "Apply Incomplete, Remaining:" + files
255 self.Set(DBUS_NAME, "status", msg)
Milton Miller0c8c5d42016-06-29 17:41:36 -0500256
Brad Bishop34c3bf92016-08-30 20:34:51 -0400257 msg = self.Get(DBUS_NAME, "status") + "\n"
258 if self.progress_name:
259 try:
260 prog = open(self.progress_name, 'r')
261 for line in prog:
262 # strip off initial sets of xxx\r here
263 # ignore crlf at the end
264 # cr will be -1 if no '\r' is found
265 cr = line.rfind("\r", 0, -2)
266 msg = msg + line[cr + 1:]
267 except OSError as e:
268 if (e.error == EEXIST):
269 pass
270 raise
271 return msg
272
273 @dbus.service.method(
274 DBUS_NAME, in_signature='', out_signature='')
275 def Apply(self):
276 progress = None
277 self.Set(DBUS_NAME, "status", "Writing images to flash")
278 try:
279 progress = tempfile.NamedTemporaryFile(
280 delete=False, prefix="progress.")
281 self.progress_name = progress.name
282 self.update_process = subprocess.Popen([
283 "/run/initramfs/update"],
284 stdout=progress.file,
285 stderr=subprocess.STDOUT)
286 except Exception as e:
287 try:
288 progress.close()
289 os.unlink(progress.name)
290 self.progress_name = None
Adriana Kobylak24341f92018-01-26 15:07:23 -0600291 except Exception:
Brad Bishop34c3bf92016-08-30 20:34:51 -0400292 pass
293 raise
294
295 try:
296 progress.close()
Adriana Kobylak24341f92018-01-26 15:07:23 -0600297 except Exception:
Brad Bishop34c3bf92016-08-30 20:34:51 -0400298 pass
299
300 @dbus.service.method(
301 DBUS_NAME, in_signature='', out_signature='')
302 def PrepareForUpdate(self):
303 subprocess.call([
304 "fw_setenv",
305 "openbmconce",
306 "copy-files-to-ram copy-base-filesystem-to-ram"])
Adriana Kobylak7bef82c2017-10-02 19:33:48 -0500307 # Set the variable twice so that it is written to both environments of
308 # the u-boot redundant environment variables since initramfs can only
309 # read one of the environments.
310 subprocess.call([
311 "fw_setenv",
312 "openbmconce",
313 "copy-files-to-ram copy-base-filesystem-to-ram"])
Brad Bishop34c3bf92016-08-30 20:34:51 -0400314 self.Set(DBUS_NAME, "status", "Switch to update mode in progress")
315 o = bus.get_object(BMC_DBUS_NAME, BMC_OBJ_NAME)
316 intf = dbus.Interface(o, BMC_DBUS_NAME)
317 intf.warmReset()
Milton Miller0c8c5d42016-06-29 17:41:36 -0500318
Norman James3d6a06f2016-01-30 13:03:45 -0600319
320if __name__ == '__main__':
321 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
322
Brad Bishop84e73b52016-05-12 15:57:52 -0400323 bus = get_dbus()
Norman James3d6a06f2016-01-30 13:03:45 -0600324 obj = BmcFlashControl(bus, OBJ_NAME)
325 mainloop = gobject.MainLoop()
Brad Bishopf0f3efe2016-06-29 23:20:24 -0400326
327 obj.unmask_signals()
Brad Bishop70852a32016-06-29 22:58:51 -0400328 name = dbus.service.BusName(DBUS_NAME, bus)
Brad Bishop34c3bf92016-08-30 20:34:51 -0400329
Norman James3d6a06f2016-01-30 13:03:45 -0600330 print "Running Bmc Flash Control"
331 mainloop.run()
Brad Bishop53066752016-09-21 08:48:04 -0400332
333# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4