1// Copyright 2016 syzkaller project authors. All rights reserved. 2// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4package main 5 6import ( 7 "fmt" 8 "html/template" 9 "net" 10 "net/http" 11 "sort" 12 "strings" 13 14 "github.com/google/syzkaller/pkg/log" 15) 16 17func (hub *Hub) initHTTP(addr string) { 18 http.HandleFunc("/", hub.httpSummary) 19 20 ln, err := net.Listen("tcp4", addr) 21 if err != nil { 22 log.Fatalf("failed to listen on %v: %v", addr, err) 23 } 24 log.Logf(0, "serving http on http://%v", ln.Addr()) 25 go func() { 26 err := http.Serve(ln, nil) 27 log.Fatalf("failed to serve http: %v", err) 28 }() 29} 30 31func (hub *Hub) httpSummary(w http.ResponseWriter, r *http.Request) { 32 hub.mu.Lock() 33 defer hub.mu.Unlock() 34 35 data := &UISummaryData{ 36 Log: log.CachedLogOutput(), 37 } 38 total := UIManager{ 39 Name: "total", 40 Corpus: len(hub.st.Corpus.Records), 41 Repros: len(hub.st.Repros.Records), 42 } 43 for name, mgr := range hub.st.Managers { 44 total.Added += mgr.Added 45 total.Deleted += mgr.Deleted 46 total.New += mgr.New 47 total.SentRepros += mgr.SentRepros 48 total.RecvRepros += mgr.RecvRepros 49 data.Managers = append(data.Managers, UIManager{ 50 Name: name, 51 Corpus: len(mgr.Corpus.Records), 52 Added: mgr.Added, 53 Deleted: mgr.Deleted, 54 New: mgr.New, 55 SentRepros: mgr.SentRepros, 56 RecvRepros: mgr.RecvRepros, 57 }) 58 } 59 sort.Sort(UIManagerArray(data.Managers)) 60 data.Managers = append([]UIManager{total}, data.Managers...) 61 if err := summaryTemplate.Execute(w, data); err != nil { 62 log.Logf(0, "failed to execute template: %v", err) 63 http.Error(w, fmt.Sprintf("failed to execute template: %v", err), http.StatusInternalServerError) 64 return 65 } 66} 67 68func compileTemplate(html string) *template.Template { 69 return template.Must(template.New("").Parse(strings.Replace(html, "{{STYLE}}", htmlStyle, -1))) 70} 71 72type UISummaryData struct { 73 Managers []UIManager 74 Log string 75} 76 77type UIManager struct { 78 Name string 79 Corpus int 80 Added int 81 Deleted int 82 New int 83 Repros int 84 SentRepros int 85 RecvRepros int 86} 87 88type UIManagerArray []UIManager 89 90func (a UIManagerArray) Len() int { return len(a) } 91func (a UIManagerArray) Less(i, j int) bool { return a[i].Name < a[j].Name } 92func (a UIManagerArray) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 93 94var summaryTemplate = compileTemplate(` 95<!doctype html> 96<html> 97<head> 98 <title>syz-hub</title> 99 {{STYLE}} 100</head> 101<body> 102<b>syz-hub</b> 103<br><br> 104 105<table> 106 <caption>Managers:</caption> 107 <tr> 108 <th>Name</th> 109 <th>Corpus</th> 110 <th>Added</th> 111 <th>Deleted</th> 112 <th>New</th> 113 <th>Repros</th> 114 <th>Sent</th> 115 <th>Recv</th> 116 </tr> 117 {{range $m := $.Managers}} 118 <tr> 119 <td>{{$m.Name}}</td> 120 <td>{{$m.Corpus}}</td> 121 <td>{{$m.Added}}</td> 122 <td>{{$m.Deleted}}</td> 123 <td>{{$m.New}}</td> 124 <td>{{$m.Repros}}</td> 125 <td>{{$m.SentRepros}}</td> 126 <td>{{$m.RecvRepros}}</td> 127 </tr> 128 {{end}} 129</table> 130<br><br> 131 132Log: 133<br> 134<textarea id="log_textarea" readonly rows="50"> 135{{.Log}} 136</textarea> 137<script> 138 var textarea = document.getElementById("log_textarea"); 139 textarea.scrollTop = textarea.scrollHeight; 140</script> 141 142</body></html> 143`) 144 145const htmlStyle = ` 146 <style type="text/css" media="screen"> 147 table { 148 border-collapse:collapse; 149 border:1px solid; 150 } 151 table caption { 152 font-weight: bold; 153 } 154 table td { 155 border:1px solid; 156 padding: 3px; 157 } 158 table th { 159 border:1px solid; 160 padding: 3px; 161 } 162 textarea { 163 width:100%; 164 } 165 </style> 166` 167