-
Notifications
You must be signed in to change notification settings - Fork 15
/
ctrlsock.go
125 lines (104 loc) · 2.83 KB
/
ctrlsock.go
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 ([email protected])
* 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
}