github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/syz-hub/http.go (about) 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 4 package main 5 6 import ( 7 "fmt" 8 "html/template" 9 "net" 10 "net/http" 11 "sort" 12 "strings" 13 14 "github.com/google/syzkaller/pkg/log" 15 ) 16 17 func (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 31 func (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 Domain: mgr.Domain, 52 Corpus: len(mgr.Corpus.Records), 53 Added: mgr.Added, 54 Deleted: mgr.Deleted, 55 New: mgr.New, 56 SentRepros: mgr.SentRepros, 57 RecvRepros: mgr.RecvRepros, 58 }) 59 } 60 sort.Slice(data.Managers, func(i, j int) bool { 61 return data.Managers[i].Name < data.Managers[j].Name 62 }) 63 data.Managers = append([]UIManager{total}, data.Managers...) 64 if err := summaryTemplate.Execute(w, data); err != nil { 65 log.Logf(0, "failed to execute template: %v", err) 66 http.Error(w, fmt.Sprintf("failed to execute template: %v", err), http.StatusInternalServerError) 67 return 68 } 69 } 70 71 func compileTemplate(html string) *template.Template { 72 return template.Must(template.New("").Parse(strings.Replace(html, "{{STYLE}}", htmlStyle, -1))) 73 } 74 75 type UISummaryData struct { 76 Managers []UIManager 77 Log string 78 } 79 80 type UIManager struct { 81 Name string 82 Domain string 83 Corpus int 84 Added int 85 Deleted int 86 New int 87 Repros int 88 SentRepros int 89 RecvRepros int 90 } 91 92 var summaryTemplate = compileTemplate(` 93 <!doctype html> 94 <html> 95 <head> 96 <title>syz-hub</title> 97 {{STYLE}} 98 </head> 99 <body> 100 <b>syz-hub</b> 101 <br><br> 102 103 <table> 104 <caption>Managers:</caption> 105 <tr> 106 <th>Name</th> 107 <th>Domain</th> 108 <th>Corpus</th> 109 <th>Added</th> 110 <th>Deleted</th> 111 <th>New</th> 112 <th>Repros</th> 113 <th>Sent</th> 114 <th>Recv</th> 115 </tr> 116 {{range $m := $.Managers}} 117 <tr> 118 <td>{{$m.Name}}</td> 119 <td>{{$m.Domain}}</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 132 Log: 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 145 const 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 `