1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
|
/* ipp-usb - HTTP reverse proxy, backed by IPP-over-USB connection to device
*
* Copyright (C) 2020 and up by Alexander Pevzner (pzz@apevzner.com)
* See LICENSE for license terms and conditions
*
* ipp-usb status support
*/
package main
import (
"bytes"
"fmt"
"io/ioutil"
"net"
"net/http"
"sort"
"strconv"
"sync"
)
// statusOfDevice represents a status of the particular device
type statusOfDevice struct {
desc UsbDeviceDesc // Device descriptor
init error // Initialization error, nil if none
HTTPPort int // Assigned http port for the device
}
var (
// statusTable maintains a per-device status,
// indexed by the UsbAddr
statusTable = make(map[UsbAddr]*statusOfDevice)
// statusLock protects access to the statusTable
statusLock sync.RWMutex
)
// StatusRetrieve connects to the running ipp-usb daemon, retrieves
// its status and returns retrieved status as a printable text
func StatusRetrieve() ([]byte, error) {
t := &http.Transport{
Dial: func(network, addr string) (net.Conn, error) {
return CtrlsockDial()
},
}
c := &http.Client{
Transport: t,
}
rsp, err := c.Get("http://localhost/status")
if err != nil {
return nil, err
}
defer rsp.Body.Close()
return ioutil.ReadAll(rsp.Body)
}
// StatusFormat formats ipp-usb status as a text
func StatusFormat() []byte {
buf := &bytes.Buffer{}
// Lock the statusTable
statusLock.RLock()
defer statusLock.RUnlock()
// Dump ipp-usb daemon status. If we are here, we are
// definitely running :-)
buf.WriteString("ipp-usb daemon: running\n")
// Sort devices by address
devs := make([]*statusOfDevice, len(statusTable))
i := 0
for _, status := range statusTable {
devs[i] = status
i++
}
sort.Slice(devs, func(i, j int) bool {
return devs[i].desc.UsbAddr.Less(devs[j].desc.UsbAddr)
})
// Format per-device status
buf.WriteString("ipp-usb devices:")
if len(statusTable) == 0 {
buf.WriteString(" not found\n")
} else {
buf.WriteString("\n")
fmt.Fprintf(buf, " Num Device Vndr:Prod Port Model\n")
for i, status := range devs {
info, _ := status.desc.GetUsbDeviceInfo()
s := "-"
if status.HTTPPort != 0 {
s = strconv.Itoa(status.HTTPPort)
}
fmt.Fprintf(buf, " %3d. %s %4.4x:%.4x %-5s %q\n",
i+1, status.desc.UsbAddr,
info.Vendor, info.Product, s,
info.MfgAndProduct)
s = "OK"
if status.init != nil {
s = devs[i].init.Error()
}
fmt.Fprintf(buf, " status: %s\n", s)
}
}
return buf.Bytes()
}
// StatusSet adds device to the status table or updates status
// of the already known device
func StatusSet(addr UsbAddr, desc UsbDeviceDesc, HTTPPort int, init error) {
statusLock.Lock()
statusTable[addr] = &statusOfDevice{
desc: desc,
init: init,
HTTPPort: HTTPPort,
}
statusLock.Unlock()
}
// StatusDel deletes device from the status table
func StatusDel(addr UsbAddr) {
statusLock.Lock()
delete(statusTable, addr)
statusLock.Unlock()
}
|