blob: eb5b9ac847f19025ebe8e304deeda36a2a341540 [file] [log] [blame]
rtc@google.comded22402009-10-26 22:36:21 +00001# Copyright (c) 2009 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
rtc@google.com64244662009-11-12 00:52:08 +00005from buildutil import BuildObject
rtc@google.comded22402009-10-26 22:36:21 +00006from xml.dom import minidom
7
8import os
9import web
10
rtc@google.com64244662009-11-12 00:52:08 +000011class Autoupdate(BuildObject):
rtc@google.comded22402009-10-26 22:36:21 +000012
rtc@google.com21a5ca32009-11-04 18:23:23 +000013 # Basic functionality of handling ChromeOS autoupdate pings
14 # and building/serving update images.
15 # TODO(rtc): Clean this code up and write some tests.
rtc@google.comded22402009-10-26 22:36:21 +000016
rtc@google.com21a5ca32009-11-04 18:23:23 +000017 def GetUpdatePayload(self, hash, size, url):
18 payload = """<?xml version="1.0" encoding="UTF-8"?>
19 <gupdate xmlns="http://www.google.com/update2/response" protocol="2.0">
20 <app appid="{%s}" status="ok">
21 <ping status="ok"/>
22 <updatecheck
23 codebase="%s"
24 hash="%s"
25 needsadmin="false"
26 size="%s"
27 status="ok"/>
28 </app>
29 </gupdate>
30 """
31 return payload % (self.app_id, url, hash, size)
rtc@google.comded22402009-10-26 22:36:21 +000032
rtc@google.com21a5ca32009-11-04 18:23:23 +000033 def GetNoUpdatePayload(self):
34 payload = """<?xml version="1.0" encoding="UTF-8"?>
35 <gupdate xmlns="http://www.google.com/update2/response" protocol="2.0">
36 <app appid="{%s}" status="ok">
37 <ping status="ok"/>
38 <updatecheck status="noupdate"/>
39 </app>
40 </gupdate>
41 """
42 return payload % self.app_id
rtc@google.comded22402009-10-26 22:36:21 +000043
Sam Leffler76382042010-02-18 09:58:42 -080044 def GetLatestImagePath(self, board_id):
45 cmd = "%s/get_latest_image.sh --board %s" % (self.scripts_dir, board_id)
rtc@google.com21a5ca32009-11-04 18:23:23 +000046 return os.popen(cmd).read().strip()
rtc@google.comded22402009-10-26 22:36:21 +000047
rtc@google.com21a5ca32009-11-04 18:23:23 +000048 def GetLatestVersion(self, latest_image_path):
49 latest_version = latest_image_path.split('/')[-1]
Ryan Cairns1b05beb2010-02-05 17:05:24 -080050
51 # Removes the portage build prefix.
52 latest_version = latest_version.lstrip("g-")
rtc@google.com21a5ca32009-11-04 18:23:23 +000053 return latest_version.split('-')[0]
rtc@google.comded22402009-10-26 22:36:21 +000054
rtc@google.com21a5ca32009-11-04 18:23:23 +000055 def CanUpdate(self, client_version, latest_version):
56 """
57 Returns true iff the latest_version is greater than the client_version.
58 """
59 client_tokens = client_version.split('.')
60 latest_tokens = latest_version.split('.')
Charlie Lee8c993082010-02-24 13:27:37 -080061 web.debug("client version %s latest version %s" \
62 % (client_version, latest_version))
rtc@google.com21a5ca32009-11-04 18:23:23 +000063 for i in range(0,4):
64 if int(latest_tokens[i]) == int(client_tokens[i]):
65 continue
66 return int(latest_tokens[i]) > int(client_tokens[i])
rtc@google.comded22402009-10-26 22:36:21 +000067 return False
rtc@google.comded22402009-10-26 22:36:21 +000068
rtc@google.com21a5ca32009-11-04 18:23:23 +000069 def BuildUpdateImage(self, image_path):
70 image_file = "%s/rootfs.image" % image_path
71 web.debug("checking image file %s/update.gz" % image_path)
72 if not os.path.exists("%s/update.gz" % image_path):
73 mkupdate = "%s/mk_memento_images.sh %s" % (self.scripts_dir, image_file)
74 web.debug(mkupdate)
75 err = os.system(mkupdate)
76 if err != 0:
77 web.debug("failed to create update image")
78 return False
rtc@google.comded22402009-10-26 22:36:21 +000079
rtc@google.com21a5ca32009-11-04 18:23:23 +000080 web.debug("Found an image, copying it to static")
81 err = os.system("cp %s/update.gz %s" % (image_path, self.static_dir))
82 if err != 0:
Charlie Lee8c993082010-02-24 13:27:37 -080083 web.debug("Unable to move update.gz from %s to %s" \
84 % (image_path, self.static_dir))
rtc@google.com21a5ca32009-11-04 18:23:23 +000085 return False
86 return True
rtc@google.comded22402009-10-26 22:36:21 +000087
rtc@google.com21a5ca32009-11-04 18:23:23 +000088 def GetSize(self, update_path):
89 return os.path.getsize(update_path)
rtc@google.comded22402009-10-26 22:36:21 +000090
rtc@google.com21a5ca32009-11-04 18:23:23 +000091 def GetHash(self, update_path):
Charlie Lee8c993082010-02-24 13:27:37 -080092 cmd = "cat %s | openssl sha1 -binary | openssl base64 | tr \'\\n\' \' \';" \
93 % update_path
rtc@google.com21a5ca32009-11-04 18:23:23 +000094 web.debug(cmd)
95 return os.popen(cmd).read()
rtc@google.comded22402009-10-26 22:36:21 +000096
rtc@google.com21a5ca32009-11-04 18:23:23 +000097 def HandleUpdatePing(self, data):
98 update_dom = minidom.parseString(data)
99 root = update_dom.firstChild
100 query = root.getElementsByTagName("o:app")[0]
Charlie Lee8c993082010-02-24 13:27:37 -0800101 client_version = query.getAttribute('version')
102 board_id = query.hasAttribute('board') and query.getAttribute('board') \
103 or "x86-generic"
104 latest_image_path = self.GetLatestImagePath(board_id)
105 latest_version = self.GetLatestVersion(latest_image_path)
106 if client_version != "ForcedUpdate" \
107 and not self.CanUpdate(client_version, latest_version):
rtc@google.com21a5ca32009-11-04 18:23:23 +0000108 web.debug("no update")
109 return self.GetNoUpdatePayload()
110
111 web.debug("update found %s " % latest_version)
112 ok = self.BuildUpdateImage(latest_image_path)
113 if ok != True:
114 web.debug("Failed to build an update image")
115 return self.GetNoUpdatePayload()
116
117 hash = self.GetHash("%s/update.gz" % self.static_dir)
118 size = self.GetSize("%s/update.gz" % self.static_dir)
119 hostname = web.ctx.host
120 url = "http://%s/static/update.gz" % hostname
121 return self.GetUpdatePayload(hash, size, url)
rtc@google.comded22402009-10-26 22:36:21 +0000122