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