swupdate.bbclass 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. # Copyright (C) 2015 Stefano Babic <sbabic@denx.de>
  2. #
  3. # Some parts from the patch class
  4. #
  5. # swupdate allows to generate a compound image for the
  6. # in the "swupdate" format, used for updating the targets
  7. # in field.
  8. # See also http://sbabic.github.io/swupdate/
  9. #
  10. #
  11. # To use, add swupdate to the inherit clause and set
  12. # set the images (all of them must be found in deploy directory)
  13. # that are part of the compound image.
  14. S = "${WORKDIR}/${PN}"
  15. DEPENDS += "${@ 'openssl-native' if d.getVar('SWUPDATE_SIGNING', True) else ''}"
  16. IMAGE_DEPENDS ?= ""
  17. def swupdate_is_hash_needed(s, filename):
  18. with open(os.path.join(s, "sw-description"), 'r') as f:
  19. for line in f:
  20. if line.find("@%s" % (filename)) != -1:
  21. return True
  22. return False
  23. def swupdate_get_sha256(s, filename):
  24. import hashlib
  25. m = hashlib.sha256()
  26. with open(os.path.join(s, filename), 'rb') as f:
  27. while True:
  28. data = f.read(1024)
  29. if not data:
  30. break
  31. m.update(data)
  32. return m.hexdigest()
  33. def swupdate_write_sha256(s, filename, hash):
  34. write_lines = []
  35. with open(os.path.join(s, "sw-description"), 'r') as f:
  36. for line in f:
  37. write_lines.append(line.replace("@%s" % (filename), hash))
  38. with open(os.path.join(s, "sw-description"), 'w+') as f:
  39. for line in write_lines:
  40. f.write(line)
  41. def swupdate_getdepends(d):
  42. def adddep(depstr, deps):
  43. for i in (depstr or "").split():
  44. if i not in deps:
  45. deps.append(i)
  46. deps = []
  47. images = (d.getVar('IMAGE_DEPENDS', True) or "").split()
  48. for image in images:
  49. adddep(image , deps)
  50. depstr = ""
  51. for dep in deps:
  52. depstr += " " + dep + ":do_build"
  53. return depstr
  54. do_swuimage[dirs] = "${TOPDIR}"
  55. do_swuimage[cleandirs] += "${S}"
  56. do_swuimage[umask] = "022"
  57. do_configure[noexec] = "1"
  58. do_compile[noexec] = "1"
  59. do_install[noexec] = "1"
  60. do_package[noexec] = "1"
  61. do_package_qa[noexec] = "1"
  62. do_packagedata[noexec] = "1"
  63. do_package_write_ipk[noexec] = "1"
  64. do_package_write_deb[noexec] = "1"
  65. do_package_write_rpm[noexec] = "1"
  66. python () {
  67. deps = " " + swupdate_getdepends(d)
  68. d.appendVarFlag('do_swuimage', 'depends', deps)
  69. }
  70. do_install () {
  71. }
  72. do_createlink () {
  73. cd ${DEPLOY_DIR_IMAGE}
  74. ln -sf ${IMAGE_NAME}.swu ${IMAGE_LINK_NAME}.swu
  75. }
  76. python do_swuimage () {
  77. import shutil
  78. workdir = d.getVar('WORKDIR', True)
  79. images = (d.getVar('SWUPDATE_IMAGES', True) or "").split()
  80. s = d.getVar('S', True)
  81. shutil.copyfile(os.path.join(workdir, "sw-description"), os.path.join(s, "sw-description"))
  82. fetch = bb.fetch2.Fetch([], d)
  83. list_for_cpio = ["sw-description"]
  84. if d.getVar('SWUPDATE_SIGNING', True):
  85. list_for_cpio.append('sw-description.sig')
  86. for url in fetch.urls:
  87. local = fetch.localpath(url)
  88. filename = os.path.basename(local)
  89. if (filename != 'sw-description'):
  90. shutil.copyfile(local, os.path.join(s, "%s" % filename ))
  91. list_for_cpio.append(filename)
  92. # SWUPDATE_IMAGES refers to images in the DEPLOY directory
  93. # If they are not there, additional file can be added
  94. # by fetching from URLs
  95. deploydir = d.getVar('DEPLOY_DIR_IMAGE', True)
  96. for image in images:
  97. fstypes = (d.getVarFlag("SWUPDATE_IMAGES_FSTYPES", image, True) or "").split()
  98. if not fstypes:
  99. fstypes = [""]
  100. for fstype in fstypes:
  101. appendmachine = d.getVarFlag("SWUPDATE_IMAGES_NOAPPEND_MACHINE", image, True)
  102. if appendmachine == None:
  103. imagebase = image + '-' + d.getVar('MACHINE', True)
  104. else:
  105. imagebase = image
  106. imagename = imagebase + fstype
  107. src = os.path.join(deploydir, "%s" % imagename)
  108. dst = os.path.join(s, "%s" % imagename)
  109. shutil.copyfile(src, dst)
  110. list_for_cpio.append(imagename)
  111. for file in list_for_cpio:
  112. if file != 'sw-description' and swupdate_is_hash_needed(s, file):
  113. hash = swupdate_get_sha256(s, file)
  114. swupdate_write_sha256(s, file, hash)
  115. signing = d.getVar('SWUPDATE_SIGNING', True)
  116. if signing == "1":
  117. bb.warn('SWUPDATE_SIGNING = "1" is deprecated, falling back to "RSA". It is advised to set it to "RSA" if using RSA signing.')
  118. signing = "RSA"
  119. if signing:
  120. if signing == "CUSTOM":
  121. sign_tool = d.getVar('SWUPDATE_SIGN_TOOL', True)
  122. if sign_tool:
  123. ret = os.system(sign_tool)
  124. if ret != 0:
  125. bb.fatal("Failed to sign with %s" % (sign_tool))
  126. else:
  127. bb.fatal("Custom SWUPDATE_SIGN_TOOL is not given")
  128. elif signing == "RSA":
  129. privkey = d.getVar('SWUPDATE_PRIVATE_KEY', True)
  130. if not privkey:
  131. bb.fatal("SWUPDATE_PRIVATE_KEY isn't set")
  132. if not os.path.exists(privkey):
  133. bb.fatal("SWUPDATE_PRIVATE_KEY %s doesn't exist" % (privkey))
  134. passout = d.getVar('SWUPDATE_PASSWORD_FILE', True)
  135. if passout:
  136. passout = "-passin file:'%s' " % (passout)
  137. else:
  138. passout = ""
  139. signcmd = "openssl dgst -sha256 -sign '%s' %s -out '%s' '%s'" % (
  140. privkey,
  141. passout,
  142. os.path.join(s, 'sw-description.sig'),
  143. os.path.join(s, 'sw-description'))
  144. if os.system(signcmd) != 0:
  145. bb.fatal("Failed to sign sw-description with %s" % (privkey))
  146. elif signing == "CMS":
  147. cms_cert = d.getVar('SWUPDATE_CMS_CERT', True)
  148. if not cms_cert:
  149. bb.fatal("SWUPDATE_CMS_CERT is not set")
  150. if not os.path.exists(cms_cert):
  151. bb.fatal("SWUPDATE_CMS_CERT %s doesn't exist" % (cms_cert))
  152. cms_key = d.getVar('SWUPDATE_CMS_KEY', True)
  153. if not cms_key:
  154. bb.fatal("SWUPDATE_CMS_KEY isn't set")
  155. if not os.path.exists(cms_key):
  156. bb.fatal("SWUPDATE_CMS_KEY %s doesn't exist" % (cms_key))
  157. signcmd = "openssl cms -sign -in '%s' -out '%s' -signer '%s' -inkey '%s' -outform DER -nosmimecap -binary" % (
  158. os.path.join(s, 'sw-description'),
  159. os.path.join(s, 'sw-description.sig'),
  160. cms_cert,
  161. cms_key)
  162. if os.system(signcmd) != 0:
  163. bb.fatal("Failed to sign sw-description with %s" % (privkey))
  164. else:
  165. bb.fatal("Unrecognized SWUPDATE_SIGNING mechanism.");
  166. line = 'for i in ' + ' '.join(list_for_cpio) + '; do echo $i;done | cpio -ov -H crc >' + os.path.join(deploydir,d.getVar('IMAGE_NAME', True) + '.swu')
  167. os.system("cd " + s + ";" + line)
  168. }
  169. COMPRESSIONTYPES = ""
  170. PACKAGE_ARCH = "${MACHINE_ARCH}"
  171. addtask do_swuimage after do_unpack before do_install
  172. addtask do_createlink after do_swuimage before do_install