package main import ( "container/ring" "fmt" "github.com/hpcloud/tail" "encoding/json" "net/http" "os" "regexp" "strconv" "strings" "time" ) func fileHandler(w http.ResponseWriter, r *http.Request, s string) { fn := r.URL.Path[5:len(r.URL.Path)] for _, v := range whitelist { if !strings.Contains(v, fn) { sendFile(w, fn, "", "Permission denied") return } } if _, err := os.Stat(fn); err != nil { sendFile(w, fn, "", "No such file or directory") return } if (watch == false) { t, err := tail.TailFile(fn, tail.Config{Follow: false}) watch = true } if (err == nil) { fmt.Println(err) } var m = "" w.WriteHeader(http.StatusOK) for line := range t.Lines { m += fmt.Sprintf("%s\n", line.Text) logring.Value = line.Text logring = logring.Next() } if m != m_prev { sendFile(w, fn, m, "") m_prev = m } } func sendFile(w http.ResponseWriter, fn string, m string, err string) { json.NewEncoder(w).Encode(map[string]string{"time": strconv.FormatInt(t_ms(), 16), "Filename": fn, "Text": m, "error": err}) } func t_ms() (int64) { now := time.Now() ns := now.UnixNano() ms := ns / 1000000 return ms } func makeHandler(fn func(http.ResponseWriter, *http.Request, string)) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { m := validPath.FindStringSubmatch(r.URL.Path) if m == nil { http.NotFound(w, r) return } fmt.Println("makeHandler::request::" + r.RequestURI) fn(w, r, m[2]) } } var prj = "httptail" var version = "0.1.0" var server_inet6 = "[fdef:c181:4234:7978:6::1]:8080" var whitelist = map[int]string{ 0: "/var/log", } var logring = ring.New(1000) var validPath = regexp.MustCompile("^/(tail*|auth)/(.*)$") var watch bool = false var m_prev = "" func main() { uid := os.Getuid() t.Filename = "" if (uid == -1) { fmt.Println("Windows is unsupported") } if (uid == 0) { fmt.Println("Refusing to run as root") return } http.HandleFunc("/tail/", makeHandler(fileHandler)) fmt.Println("Serving " + prj + " " + version + " on " + server_inet6) http.ListenAndServe(server_inet6, nil) t.Cleanup() }