out-of-tree kernel {module, exploit} development tool
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

db.go 6.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. // Copyright 2019 Mikhail Klementev. All rights reserved.
  2. // Use of this source code is governed by a AGPLv3 license
  3. // (or later) that can be found in the LICENSE file.
  4. package main
  5. import (
  6. "database/sql"
  7. "fmt"
  8. "strconv"
  9. "time"
  10. _ "github.com/mattn/go-sqlite3"
  11. "code.dumpstack.io/tools/out-of-tree/config"
  12. "code.dumpstack.io/tools/out-of-tree/qemu"
  13. )
  14. // Change on ANY database update
  15. const currentDatabaseVersion = 2
  16. const versionField = "db_version"
  17. type logEntry struct {
  18. ID int
  19. Tag string
  20. Timestamp time.Time
  21. qemu.System
  22. config.Artifact
  23. config.KernelInfo
  24. phasesResult
  25. }
  26. func createLogTable(db *sql.DB) (err error) {
  27. _, err = db.Exec(`
  28. CREATE TABLE IF NOT EXISTS log (
  29. id INTEGER PRIMARY KEY,
  30. time DATETIME DEFAULT CURRENT_TIMESTAMP,
  31. tag TEXT,
  32. name TEXT,
  33. type TEXT,
  34. distro_type TEXT,
  35. distro_release TEXT,
  36. kernel_release TEXT,
  37. build_output TEXT,
  38. build_ok BOOLEAN,
  39. run_output TEXT,
  40. run_ok BOOLEAN,
  41. test_output TEXT,
  42. test_ok BOOLEAN,
  43. qemu_stdout TEXT,
  44. qemu_stderr TEXT,
  45. kernel_panic BOOLEAN,
  46. timeout_kill BOOLEAN
  47. )`)
  48. return
  49. }
  50. func createMetadataTable(db *sql.DB) (err error) {
  51. _, err = db.Exec(`
  52. CREATE TABLE IF NOT EXISTS metadata (
  53. id INTEGER PRIMARY KEY,
  54. key TEXT UNIQUE,
  55. value TEXT
  56. )`)
  57. return
  58. }
  59. func metaChkValue(db *sql.DB, key string) (exist bool, err error) {
  60. sql := "SELECT EXISTS(SELECT id FROM metadata WHERE key = $1)"
  61. stmt, err := db.Prepare(sql)
  62. if err != nil {
  63. return
  64. }
  65. defer stmt.Close()
  66. err = stmt.QueryRow(key).Scan(&exist)
  67. return
  68. }
  69. func metaGetValue(db *sql.DB, key string) (value string, err error) {
  70. stmt, err := db.Prepare("SELECT value FROM metadata " +
  71. "WHERE key = $1")
  72. if err != nil {
  73. return
  74. }
  75. defer stmt.Close()
  76. err = stmt.QueryRow(key).Scan(&value)
  77. return
  78. }
  79. func metaSetValue(db *sql.DB, key, value string) (err error) {
  80. stmt, err := db.Prepare("INSERT OR REPLACE INTO metadata " +
  81. "(key, value) VALUES ($1, $2)")
  82. if err != nil {
  83. return
  84. }
  85. defer stmt.Close()
  86. _, err = stmt.Exec(key, value)
  87. return
  88. }
  89. func getVersion(db *sql.DB) (version int, err error) {
  90. s, err := metaGetValue(db, versionField)
  91. if err != nil {
  92. return
  93. }
  94. version, err = strconv.Atoi(s)
  95. return
  96. }
  97. func addToLog(db *sql.DB, q *qemu.System, ka config.Artifact,
  98. ki config.KernelInfo, res *phasesResult, tag string) (err error) {
  99. stmt, err := db.Prepare("INSERT INTO log (name, type, tag, " +
  100. "distro_type, distro_release, kernel_release, " +
  101. "build_output, build_ok, " +
  102. "run_output, run_ok, " +
  103. "test_output, test_ok, " +
  104. "qemu_stdout, qemu_stderr, " +
  105. "kernel_panic, timeout_kill) " +
  106. "VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, " +
  107. "$10, $11, $12, $13, $14, $15, $16);")
  108. if err != nil {
  109. return
  110. }
  111. defer stmt.Close()
  112. _, err = stmt.Exec(
  113. ka.Name, ka.Type, tag,
  114. ki.DistroType, ki.DistroRelease, ki.KernelRelease,
  115. res.Build.Output, res.Build.Ok,
  116. res.Run.Output, res.Run.Ok,
  117. res.Test.Output, res.Test.Ok,
  118. q.Stdout, q.Stderr,
  119. q.KernelPanic, q.KilledByTimeout,
  120. )
  121. if err != nil {
  122. return
  123. }
  124. return
  125. }
  126. func getAllLogs(db *sql.DB, tag string, num int) (les []logEntry, err error) {
  127. stmt, err := db.Prepare("SELECT id, time, name, type, tag, " +
  128. "distro_type, distro_release, kernel_release, " +
  129. "build_ok, run_ok, test_ok, kernel_panic, " +
  130. "timeout_kill FROM log ORDER BY datetime(time) DESC " +
  131. "LIMIT $1")
  132. if err != nil {
  133. return
  134. }
  135. defer stmt.Close()
  136. rows, err := stmt.Query(num)
  137. if err != nil {
  138. return
  139. }
  140. defer rows.Close()
  141. for rows.Next() {
  142. le := logEntry{}
  143. err = rows.Scan(&le.ID, &le.Timestamp,
  144. &le.Name, &le.Type, &le.Tag,
  145. &le.DistroType, &le.DistroRelease, &le.KernelRelease,
  146. &le.Build.Ok, &le.Run.Ok, &le.Test.Ok,
  147. &le.KernelPanic, &le.KilledByTimeout,
  148. )
  149. if err != nil {
  150. return
  151. }
  152. if tag == "" || tag == le.Tag {
  153. les = append(les, le)
  154. }
  155. }
  156. return
  157. }
  158. func getAllArtifactLogs(db *sql.DB, tag string, num int, ka config.Artifact) (
  159. les []logEntry, err error) {
  160. stmt, err := db.Prepare("SELECT id, time, name, type, tag, " +
  161. "distro_type, distro_release, kernel_release, " +
  162. "build_ok, run_ok, test_ok, kernel_panic, " +
  163. "timeout_kill FROM log WHERE name=$1 AND type=$2 " +
  164. "ORDER BY datetime(time) DESC LIMIT $3")
  165. if err != nil {
  166. return
  167. }
  168. defer stmt.Close()
  169. rows, err := stmt.Query(ka.Name, ka.Type, num)
  170. if err != nil {
  171. return
  172. }
  173. defer rows.Close()
  174. for rows.Next() {
  175. le := logEntry{}
  176. err = rows.Scan(&le.ID, &le.Timestamp,
  177. &le.Name, &le.Type, &le.Tag,
  178. &le.DistroType, &le.DistroRelease, &le.KernelRelease,
  179. &le.Build.Ok, &le.Run.Ok, &le.Test.Ok,
  180. &le.KernelPanic, &le.KilledByTimeout,
  181. )
  182. if err != nil {
  183. return
  184. }
  185. if tag == "" || tag == le.Tag {
  186. les = append(les, le)
  187. }
  188. }
  189. return
  190. }
  191. func getLogByID(db *sql.DB, id int) (le logEntry, err error) {
  192. stmt, err := db.Prepare("SELECT id, time, name, type, tag, " +
  193. "distro_type, distro_release, kernel_release, " +
  194. "build_ok, run_ok, test_ok, " +
  195. "build_output, run_output, test_output, " +
  196. "qemu_stdout, qemu_stderr, " +
  197. "kernel_panic, timeout_kill " +
  198. "FROM log WHERE id=$1")
  199. if err != nil {
  200. return
  201. }
  202. defer stmt.Close()
  203. err = stmt.QueryRow(id).Scan(&le.ID, &le.Timestamp,
  204. &le.Name, &le.Type, &le.Tag,
  205. &le.DistroType, &le.DistroRelease, &le.KernelRelease,
  206. &le.Build.Ok, &le.Run.Ok, &le.Test.Ok,
  207. &le.Build.Output, &le.Run.Output, &le.Test.Output,
  208. &le.Stdout, &le.Stderr,
  209. &le.KernelPanic, &le.KilledByTimeout,
  210. )
  211. return
  212. }
  213. func createSchema(db *sql.DB) (err error) {
  214. err = createMetadataTable(db)
  215. if err != nil {
  216. return
  217. }
  218. err = createLogTable(db)
  219. if err != nil {
  220. return
  221. }
  222. return
  223. }
  224. func openDatabase(path string) (db *sql.DB, err error) {
  225. db, err = sql.Open("sqlite3", path)
  226. if err != nil {
  227. return
  228. }
  229. db.SetMaxOpenConns(1)
  230. exists, _ := metaChkValue(db, versionField)
  231. if !exists {
  232. err = createSchema(db)
  233. if err != nil {
  234. return
  235. }
  236. err = metaSetValue(db, versionField,
  237. strconv.Itoa(currentDatabaseVersion))
  238. return
  239. }
  240. version, err := getVersion(db)
  241. if err != nil {
  242. return
  243. }
  244. if version == 1 {
  245. _, err = db.Exec(`ALTER TABLE log ADD tag TEXT`)
  246. if err != nil {
  247. return
  248. }
  249. err = metaSetValue(db, versionField, "2")
  250. if err != nil {
  251. return
  252. }
  253. version = 2
  254. }
  255. if version != currentDatabaseVersion {
  256. err = fmt.Errorf("Database is not supported (%d instead of %d)",
  257. version, currentDatabaseVersion)
  258. return
  259. }
  260. return
  261. }