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
|
/* 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
*
* Control socket handler
*
* ipp-usb runs a HTTP server on a top of the unix domain control
* socket.
*
* Currently it is only used to obtain a per-device status from the
* running daemon. Using HTTP here sounds as overkill, but taking
* in account that it costs us virtually nothing and this mechanism
* is well-extendable, this is a good choice
*/
package main
import (
"log"
"net"
"net/http"
"os"
"syscall"
)
var (
// CtrlsockAddr contains control socket address in
// a form of the net.UnixAddr structure
CtrlsockAddr = &net.UnixAddr{Name: PathControlSocket, Net: "unix"}
// ctrlsockServer is a HTTP server that runs on a top of
// the status socket
ctrlsockServer = http.Server{
Handler: http.HandlerFunc(ctrlsockHandler),
ErrorLog: log.New(Log.LineWriter(LogError, '!'), "", 0),
}
)
// ctrlsockHandler handles HTTP requests that come over the
// control socket
func ctrlsockHandler(w http.ResponseWriter, r *http.Request) {
Log.Debug(' ', "ctrlsock: %s %s", r.Method, r.URL)
// Catch panics to log
defer func() {
v := recover()
if v != nil {
Log.Panic(v)
}
}()
// Check request method
if r.Method != "GET" {
http.Error(w, r.Method+": method not supported",
http.StatusMethodNotAllowed)
return
}
// Check request path
if r.URL.Path != "/status" {
http.Error(w, "Not found", http.StatusNotFound)
return
}
// Handle the request
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
httpNoCache(w)
w.WriteHeader(http.StatusOK)
w.Write(StatusFormat())
}
// CtrlsockStart starts control socket server
func CtrlsockStart() error {
Log.Debug(' ', "ctrlsock: listening at %q", PathControlSocket)
// Listen the socket
os.Remove(PathControlSocket)
listener, err := net.ListenUnix("unix", CtrlsockAddr)
if err != nil {
return err
}
// Make socket accessible to everybody. Error is ignores,
// it's not a reason to abort ipp-usb
os.Chmod(PathControlSocket, 0777)
// Start HTTP server on a top of the listening socket
go func() {
ctrlsockServer.Serve(listener)
}()
return nil
}
// CtrlsockStop stops the control socket server
func CtrlsockStop() {
Log.Debug(' ', "ctrlsock: shutdown")
ctrlsockServer.Close()
}
// CtrlsockDial connects to the control socket of the running
// ipp-usb daemon
func CtrlsockDial() (net.Conn, error) {
conn, err := net.DialUnix("unix", nil, CtrlsockAddr)
if err == nil {
return conn, err
}
if neterr, ok := err.(*net.OpError); ok {
if syserr, ok := neterr.Err.(*os.SyscallError); ok {
switch syserr.Err {
case syscall.ECONNREFUSED, syscall.ENOENT:
err = ErrNoIppUsb
case syscall.EACCES, syscall.EPERM:
err = ErrAccess
}
}
}
return conn, err
}
|