summaryrefslogtreecommitdiff
path: root/src/util
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@sbcglobal.net>2016-11-01 14:46:15 -0400
committerLuke Shumaker <lukeshu@sbcglobal.net>2016-11-01 14:46:15 -0400
commitc7eea383aeaf6748daf994e9e28e4d0c25350736 (patch)
treea0979cbd4e6be06385fd0340e050147fdacd6e35 /src/util
initial commit
Diffstat (limited to 'src/util')
-rw-r--r--src/util/fd.go101
-rw-r--r--src/util/http.go63
-rw-r--r--src/util/template.go56
3 files changed, 220 insertions, 0 deletions
diff --git a/src/util/fd.go b/src/util/fd.go
new file mode 100644
index 0000000..17c64cd
--- /dev/null
+++ b/src/util/fd.go
@@ -0,0 +1,101 @@
+// Copyright 2015-2016 Luke Shumaker
+
+package util
+
+import (
+ "net"
+ "os"
+ "strings"
+ "fmt"
+ "sync"
+ "strconv"
+
+ sd "lukeshu.com/git/go/libsystemd/sd_daemon"
+)
+
+var fdsLock sync.Mutex
+var fds = map[int]*os.File{}
+var sdFds = map[string]int{}
+
+func init() {
+ fds[0] = os.Stdin
+ fds[1] = os.Stdout
+ fds[2] = os.Stderr
+ fromSd := sd.ListenFds(true)
+ if fromSd == nil {
+ return
+ }
+ for i, file := range fromSd {
+ fds[i+3] = file
+ sdFds[file.Name()] = i+3
+ }
+}
+
+func FdNameToNum(name string) int {
+ switch name {
+ case "stdin":
+ return 0
+ case "stdout":
+ return 1
+ case "stderr":
+ return 2
+ case "systemd":
+ if len(sdFds) == 0 {
+ return -1
+ }
+ return 3
+ default:
+ if n, err := strconv.Atoi(name); err == nil {
+ if n >= 0 {
+ return n
+ }
+ } else if strings.HasPrefix(name, "systemd:") {
+ name = strings.TrimPrefix(name, "systemd")
+ n, ok := sdFds[name]
+ if ok {
+ return n
+ } else if n, err := strconv.Atoi(name); err == nil && n < len(sdFds) {
+ return n+3
+ }
+ }
+ return -1
+ }
+}
+
+func FdFile(fd int) *os.File {
+ fdsLock.Lock()
+ defer fdsLock.Unlock()
+ file, ok := fds[fd]
+ if ok {
+ return file
+ }
+ file = os.NewFile(uintptr(fd), fmt.Sprintf("/dev/fd/%d", fd))
+ fds[fd] = file
+ return file
+}
+
+func StreamListener(stype, saddr string) (net.Listener, error) {
+ switch stype {
+ case "fd":
+ return net.FileListener(FdFile(FdNameToNum(saddr)))
+ default: /* case "tcp", "tcp4", "tcp6", "unix", "unixpacket": */
+ return net.Listen(stype, saddr)
+ }
+}
+
+func PacketListener(stype, saddr string) (net.PacketConn, error) {
+ switch stype {
+ case "fd":
+ return net.FilePacketConn(FdFile(FdNameToNum(saddr)))
+ default: /* case "udp", "udp4", "udp6", "ip", "ip4", "ip6", "unixgram": */
+ return net.ListenPacket(stype, saddr)
+ }
+}
+
+// For completeless, I might want to implement methods for each of
+// these:
+// - FIFO
+// - Special
+// - Netlink
+// - MessageQueue
+// - USBFunction
diff --git a/src/util/http.go b/src/util/http.go
new file mode 100644
index 0000000..6c216cf
--- /dev/null
+++ b/src/util/http.go
@@ -0,0 +1,63 @@
+package util
+
+import (
+ "bytes"
+ "fmt"
+ "log"
+ "net/http"
+ "runtime"
+ "strconv"
+)
+
+type httpResponseBuffer struct {
+ inner http.ResponseWriter
+ buf bytes.Buffer
+ status int
+}
+
+func (o *httpResponseBuffer) Header() http.Header {
+ return o.inner.Header()
+}
+
+func (o *httpResponseBuffer) Write(data []byte) (int, error) {
+ return o.buf.Write(data)
+}
+
+func (o *httpResponseBuffer) WriteHeader(status int) {
+ o.status = status
+}
+
+type SaneHTTPHandler struct {
+ Inner http.Handler
+}
+
+func (h SaneHTTPHandler) ServeHTTP(out http.ResponseWriter, in *http.Request) {
+ HTTPHandlerFuncWrapper(out, in, h.Inner.ServeHTTP)
+}
+
+func HTTPHandlerFuncWrapper(out http.ResponseWriter, in *http.Request, fn http.HandlerFunc) {
+ buf := httpResponseBuffer{
+ inner: out,
+ status: http.StatusOK,
+ }
+ ok := true
+ func() {
+ defer func() {
+ if r := recover(); r != nil {
+ const size = 64 << 10
+ buf := make([]byte, size)
+ buf = buf[:runtime.Stack(buf, false)]
+ st := fmt.Sprintf("%[1]T(%#[1]v) => %[1]v\n\n%[2]s", r, string(buf))
+ log.Printf("panic serving %v\n\n%s", in.URL, st)
+ http.Error(out, fmt.Sprintf("500 Internal Server Error:\n\n%s", st), 500)
+ ok = false
+ }
+ }()
+ fn(&buf, in)
+ }()
+ if ok {
+ out.Header().Set("Content-Length", strconv.Itoa(buf.buf.Len()))
+ out.WriteHeader(buf.status)
+ buf.buf.WriteTo(out)
+ }
+}
diff --git a/src/util/template.go b/src/util/template.go
new file mode 100644
index 0000000..f42875a
--- /dev/null
+++ b/src/util/template.go
@@ -0,0 +1,56 @@
+package util
+
+import (
+ "path"
+ "text/template"
+ "time"
+)
+
+func NewTemplate(filenames ...string) *template.Template {
+ return template.Must(template.New(path.Base(filenames[0])).
+ Funcs(template.FuncMap{
+ // Form input helpers
+ "value": func(v interface{}) string {
+ if v == nil {
+ return ""
+ }
+ return "value=\"" + template.HTMLEscapeString(v.(string)) + "\""
+ },
+ "checked": func(v1 interface{}, v2 interface{}) string {
+ if v1 == nil || v2 == nil {
+ return ""
+ }
+ if v1 == v2 {
+ return "checked"
+ }
+ return ""
+ },
+ "selected": func(v1 interface{}, v2 interface{}) string {
+ if v1 == nil || v2 == nil {
+ return ""
+ }
+ if v1 == v2 {
+ return "selected"
+ }
+ return ""
+ },
+ // Form result helpers
+ "q": func(v interface{}) string {
+ if v == nil || v.(string) == "" {
+ return "<output class=none>none</output>"
+ }
+ return "<output><q>" + template.HTMLEscapeString(v.(string)) + "</q></output>"
+ },
+ "have": func(v interface{}) bool {
+ return v != nil && v.(string) == "on"
+ },
+ "date": func(v string) string {
+ t, err := time.Parse("2006-01-02", v)
+ if err != nil {
+ panic(err)
+ }
+ return t.Format("January 2, 2006")
+ },
+ }).
+ ParseFiles(filenames...))
+}