add 'pull' task
WIP: add initrc parser test to analyze system boot sequencepull/20/head
parent
1f7476d884
commit
6505fea379
@ -0,0 +1,321 @@
|
||||
import org.junit.Test
|
||||
import java.io.File
|
||||
import java.util.regex.Matcher
|
||||
import java.util.regex.Pattern
|
||||
|
||||
class ReadTest {
|
||||
data class Trigger(
|
||||
var trigger: String = "",
|
||||
var actions: MutableList<String> = mutableListOf()
|
||||
)
|
||||
|
||||
data class Import(
|
||||
var initrc: String = ""
|
||||
)
|
||||
|
||||
data class Service(
|
||||
var name: String = "",
|
||||
var cmd: String = "",
|
||||
var theClass: String = "default",
|
||||
var theUser: String? = null,
|
||||
var theGroup: String? = null,
|
||||
var theSeclabel: String? = null,
|
||||
var theMiscAttr: MutableList<String> = mutableListOf(),
|
||||
var theCaps: MutableList<String> = mutableListOf(),
|
||||
var theSocket: String? = null,
|
||||
var theWritePid: String? = null,
|
||||
var theKeycodes: String? = null,
|
||||
var thePriority: Int? = null,
|
||||
var theIOPriority: String? = null,
|
||||
var theOnRestart: MutableList<String> = mutableListOf()
|
||||
)
|
||||
|
||||
fun parseConfig(inRootDir: String, inPath: String,
|
||||
triggers: MutableList<Trigger>,
|
||||
services: MutableList<Service>) {
|
||||
if (!File(inRootDir + inPath).exists()) {
|
||||
println("Parsing " + inPath + " fail: 404");
|
||||
}
|
||||
if (File(inRootDir + inPath).isFile()) {
|
||||
parseConfigFile(inRootDir, inPath, triggers, services)
|
||||
} else if (File(inRootDir + inPath).isDirectory()) {
|
||||
parseConfigDir(inRootDir, inPath, triggers, services)
|
||||
}
|
||||
}
|
||||
|
||||
fun parseConfigDir(inRootDir: String, inPath: String,
|
||||
triggers: MutableList<Trigger>,
|
||||
services: MutableList<Service>) {
|
||||
println("Parsing directory $inPath ...")
|
||||
File(inRootDir + inPath).listFiles().forEach {
|
||||
parseConfig(inRootDir,
|
||||
it.path.substring(inRootDir.length - 1),
|
||||
triggers, services)
|
||||
}
|
||||
}
|
||||
|
||||
fun parseConfigFile(inRootDir: String, inPath: String,
|
||||
triggers: MutableList<Trigger>,
|
||||
services: MutableList<Service>) {
|
||||
if (!File(inRootDir + inPath).exists()) {
|
||||
println("Parsing $inPath fail: 404");
|
||||
return
|
||||
}
|
||||
println("Parsing file $inPath ...")
|
||||
var imports: MutableList<Import> = mutableListOf()
|
||||
var aTrigger: Trigger? = null
|
||||
var aService: Service? = null
|
||||
var aImport: Import? = null
|
||||
var toBeContinued = false
|
||||
val lines = File(inRootDir + inPath).readLines()
|
||||
for (item in lines) {
|
||||
val line = item.trim();
|
||||
//comment
|
||||
if (line.startsWith("#") || line.isEmpty()) {
|
||||
continue
|
||||
}
|
||||
//continue
|
||||
if (toBeContinued) {
|
||||
if (line.endsWith("\\")) {
|
||||
aService!!.cmd += " "
|
||||
aService!!.cmd += line.substring(0, line.length - 1)
|
||||
println(" CONTINUE:" + line.substring(0, line.length - 1))
|
||||
} else {
|
||||
toBeContinued = false
|
||||
aService!!.cmd += " "
|
||||
aService!!.cmd += line
|
||||
println(" END :$line")
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
val finderOn = Pattern.compile("^(on)\\s+(\\S+.*$)").matcher(line)
|
||||
val finderService = Pattern.compile("^service\\s+(\\S+)\\s+(.*$)").matcher(line)
|
||||
val finderImport = Pattern.compile("^import\\s+(\\S+)$").matcher(line)
|
||||
if (finderOn.matches() || finderService.matches() || finderImport.matches()) {
|
||||
//flush start >>
|
||||
aTrigger?.let { /* println("[add] " + aTrigger); */ triggers.add(aTrigger!!); aTrigger = null }
|
||||
aService?.let { /* println("[add] " + aService); */ services.add(aService!!); aService = null }
|
||||
aImport?.let { /* println("[add] " + aImport); */ imports.add(aImport!!); aImport = null }
|
||||
// << flush end
|
||||
}
|
||||
finderOn.reset()
|
||||
finderService.reset()
|
||||
finderImport.reset()
|
||||
|
||||
if (finderOn.find()) {
|
||||
//println(" |on| " + line)
|
||||
//println(" group.cnt = " + finderOn.groupCount())
|
||||
//println(" " + line.substring(finderOn.start(), finderOn.end()))
|
||||
//println(" >" + finderOn.group(1))
|
||||
//println(" >" + finderOn.group(2))
|
||||
aTrigger = Trigger(trigger = finderOn.group(2))
|
||||
} else if (finderService.find()) {
|
||||
aService = Service()
|
||||
aService!!.name = finderService.group(1)
|
||||
aService!!.cmd = finderService.group(2)
|
||||
if (finderService.group(2).endsWith("\\")) { //remove trailing slash
|
||||
toBeContinued = true
|
||||
aService!!.cmd = aService!!.cmd.substring(0, aService!!.cmd.length - 1)
|
||||
}
|
||||
} else if (finderImport.find()) {
|
||||
aImport = Import()
|
||||
aImport!!.initrc = finderImport.group(1)
|
||||
if (aImport!!.initrc.startsWith("/")) {
|
||||
aImport!!.initrc = aImport!!.initrc.substring(1)
|
||||
} else {
|
||||
//do nothing
|
||||
}
|
||||
val ro_hardware = "\${ro.hardware}"
|
||||
val ro_zygote = "\${ro.zygote}"
|
||||
aImport!!.initrc = aImport!!.initrc.replace(ro_hardware, "sequoia")
|
||||
aImport!!.initrc = aImport!!.initrc.replace(ro_zygote, "zygote32")
|
||||
} else {
|
||||
if (aTrigger != null) {
|
||||
aTrigger!!.actions.add(line)
|
||||
} else if (aService != null) {
|
||||
//class
|
||||
var bParsed = false
|
||||
lateinit var mm: Matcher
|
||||
if (!bParsed) {
|
||||
mm = Pattern.compile("^class\\s+(.*)").matcher(line)
|
||||
if (mm.matches()) {
|
||||
aService!!.theClass = mm.group(1)
|
||||
bParsed = true
|
||||
}
|
||||
}
|
||||
if (!bParsed) {
|
||||
//user
|
||||
mm = Pattern.compile("^user\\s+(.*)").matcher(line)
|
||||
if (mm.matches()) {
|
||||
aService!!.theUser = mm.group(1)
|
||||
bParsed = true
|
||||
}
|
||||
}
|
||||
if (!bParsed) {
|
||||
//capabilities
|
||||
mm = Pattern.compile("^capabilities\\s+(.*)").matcher(line)
|
||||
if (mm.matches()) {
|
||||
aService!!.theCaps.add(mm.group(1))
|
||||
bParsed = true
|
||||
}
|
||||
}
|
||||
if (!bParsed) {
|
||||
//group
|
||||
mm = Pattern.compile("^group\\s+(.*)").matcher(line)
|
||||
if (mm.matches()) {
|
||||
aService!!.theGroup = mm.group(1)
|
||||
bParsed = true
|
||||
}
|
||||
}
|
||||
if (!bParsed) {
|
||||
//seclabel
|
||||
mm = Pattern.compile("^seclabel\\s+(.*)").matcher(line)
|
||||
if (mm.matches()) {
|
||||
aService!!.theSeclabel = mm.group(1)
|
||||
bParsed = true
|
||||
}
|
||||
}
|
||||
if (!bParsed) {
|
||||
//writepid
|
||||
mm = Pattern.compile("^writepid\\s+(.*)$").matcher(line)
|
||||
if (mm.matches()) {
|
||||
aService!!.theWritePid = mm.group(1)
|
||||
bParsed = true
|
||||
}
|
||||
}
|
||||
if (!bParsed) {
|
||||
//onrestart
|
||||
mm = Pattern.compile("^onrestart\\s+(.*)$").matcher(line)
|
||||
if (mm.matches()) {
|
||||
aService!!.theOnRestart.add(mm.group(1))
|
||||
bParsed = true
|
||||
}
|
||||
}
|
||||
if (!bParsed) {
|
||||
//socket
|
||||
mm = Pattern.compile("^socket\\s+(.*)$").matcher(line)
|
||||
if (mm.matches()) {
|
||||
aService!!.theSocket = mm.group(1)
|
||||
bParsed = true
|
||||
}
|
||||
}
|
||||
if (!bParsed) {
|
||||
//ioprio
|
||||
mm = Pattern.compile("^ioprio\\s+(.*)$").matcher(line)
|
||||
if (mm.matches()) {
|
||||
aService!!.theIOPriority = mm.group(1)
|
||||
bParsed = true
|
||||
}
|
||||
}
|
||||
if (!bParsed) {
|
||||
//priority
|
||||
mm = Pattern.compile("^priority\\s+(\\S+)$").matcher(line)
|
||||
if (mm.matches()) {
|
||||
aService!!.thePriority = Integer.parseInt(mm.group(1))
|
||||
bParsed = true
|
||||
}
|
||||
}
|
||||
if (!bParsed) {
|
||||
//check space
|
||||
mm = Pattern.compile("^\\S+$").matcher(line)
|
||||
if (mm.matches()) {
|
||||
aService!!.theMiscAttr.add(line)
|
||||
bParsed = true
|
||||
}
|
||||
}
|
||||
if (!bParsed) {
|
||||
println("<< Dangling << $line")
|
||||
}
|
||||
} else {
|
||||
println("<< Dangling << $line")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//flush start >>
|
||||
aTrigger?.let { /* println("[add] " + aTrigger); */ triggers.add(aTrigger!!); aTrigger = null }
|
||||
aService?.let { /* println("[add] " + aService); */ services.add(aService!!); aService = null }
|
||||
aImport?.let { /* println("[add] " + aImport); */ imports.add(aImport!!); aImport = null }
|
||||
// << flush end
|
||||
|
||||
imports.forEach { println(it) }
|
||||
|
||||
//parse imports again
|
||||
var iteratorImport: Iterator<Import> = imports.iterator()
|
||||
while (iteratorImport.hasNext()) {
|
||||
val item: Import = iteratorImport.next()
|
||||
parseConfigFile(inRootDir, item.initrc, triggers, services)
|
||||
}
|
||||
println("Parsing file $inPath done")
|
||||
}
|
||||
|
||||
fun queueEventTrigger(inServices: MutableList<Service>,
|
||||
inTriggers: List<Trigger>, inTriggerName: String,
|
||||
inIndent: String = "") {
|
||||
val aPre = inIndent
|
||||
inTriggers.filter { it.trigger == inTriggerName }.forEach { aTrigger ->
|
||||
println(aPre + " (on+${aTrigger.trigger})")
|
||||
aTrigger.actions.forEach { aAction ->
|
||||
aAction.executeCmd(inServices, inTriggers, aPre + " ")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun String.executeCmd(inServices: MutableList<Service>,
|
||||
inTriggers: List<Trigger>, inIndent: String) {
|
||||
val aPre = inIndent + " "
|
||||
if (this.startsWith("trigger ")) {
|
||||
println(aPre + "|-- " + this)
|
||||
queueEventTrigger(inServices, inTriggers, this.substring(8).trim(), aPre + "| ")
|
||||
} else if (this.startsWith("chmod")) {
|
||||
} else if (this.startsWith("chown")) {
|
||||
} else if (this.startsWith("mkdir")) {
|
||||
} else if (this.startsWith("write")) {
|
||||
} else if (Pattern.compile("class_start\\s+\\S+").matcher(this).find()) {
|
||||
println(aPre + "|-- " + this)
|
||||
val m = Pattern.compile("class_start\\s+(\\S+)$").matcher(this)
|
||||
if (m.find()) {
|
||||
inServices
|
||||
.filter {
|
||||
it.theClass != null
|
||||
&& it.theClass!!.split(" ").contains(m.group(1))
|
||||
}
|
||||
.forEach {
|
||||
println(aPre + "| \\-- Starting " + it.name + "...")
|
||||
}
|
||||
} else {
|
||||
println("error")
|
||||
}
|
||||
} else if (this.startsWith("start")) {
|
||||
println(aPre + "|-- " + this)
|
||||
println(aPre + "| \\-- Starting " + this.substring(5).trim() + "...")
|
||||
} else {
|
||||
println(aPre + "|-- " + this)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseTest() {
|
||||
System.out.println(System.getProperty("user.dir"))
|
||||
var gTriggers: MutableList<Trigger> = mutableListOf()
|
||||
var gServices: MutableList<Service> = mutableListOf()
|
||||
|
||||
parseConfig("__temp/", "/init.rc", gTriggers, gServices)
|
||||
parseConfig("__temp/", "/system/etc/init", gTriggers, gServices)
|
||||
parseConfig("__temp/", "/vendor/etc/init", gTriggers, gServices)
|
||||
parseConfig("__temp/", "/odm/etc/init", gTriggers, gServices)
|
||||
|
||||
gTriggers.forEach { println(it) }
|
||||
gServices.forEach { println(it) }
|
||||
|
||||
println("Trigger count:" + gTriggers.size)
|
||||
println("Service count:" + gServices.size)
|
||||
|
||||
queueEventTrigger(gServices, gTriggers, "early-init")
|
||||
queueEventTrigger(gServices, gTriggers, "init")
|
||||
queueEventTrigger(gServices, gTriggers, "late-init")
|
||||
// println(">> mount_all() returned 0, trigger nonencrypted")
|
||||
// queueEventTrigger(gServices, gTriggers, "nonencrypted")
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
Enable developer debugging for AVB-enabled devices
|
||||
|
||||
### modify build/unzip_boot/vbmeta.img.avb.json
|
||||
|
||||
* disable dm-verity hashtree verification
|
||||
header -> flags: set to 1(AVB_VBMETA_IMAGE_FLAGS_HASHTREE_DISABLED = 1) to disable dm-verity hashtree(system/vendor etc.)
|
||||
|
||||
```diff
|
||||
"descriptors_size" : 1384,
|
||||
"rollback_index" : 0,
|
||||
- "flags" : 0,
|
||||
+ "flags" : 1,
|
||||
"release_string" : "avbtool 1.0.0"
|
||||
},
|
||||
"authBlob" : {
|
||||
```
|
||||
|
||||
* disable all AVB verification
|
||||
header -> flags: set to 2(AVB_VBMETA_IMAGE_FLAGS_VERIFICATION_DISABLED = 2) to disable all verification, including AVB hash_footer(boot/recovery etc.) and dm-verity hashtree(system/vendor etc.)
|
||||
|
||||
```diff
|
||||
"descriptors_size" : 1384,
|
||||
"rollback_index" : 0,
|
||||
- "flags" : 0,
|
||||
+ "flags" : 2,
|
||||
"release_string" : "avbtool 1.0.0"
|
||||
},
|
||||
"authBlob" : {
|
||||
```
|
||||
|
||||
### unlock bootloader
|
||||
'unlock' state will pass flag AVB_SLOT_VERIFY_FLAGS_ALLOW_VERIFICATION_ERROR when verifying images, then you can disable
|
||||
|
||||
|
Loading…
Reference in New Issue