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 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
|
/*
* Functions in this file are used to forward syslog messages to the
* host and must be quite careful about their own logging. In general
* error messages should go via the console log.Logger defined in this
* file.
*/
package main
import (
"errors"
"fmt"
"log"
"net"
"os"
"strconv"
"strings"
"github.com/linuxkit/virtsock/pkg/hvsock"
"github.com/linuxkit/virtsock/pkg/vsock"
)
var (
console *log.Logger
currentConn vConn
alreadyConnectedOnce bool
/* When writing we don't discover e.g. EPIPE until the _next_
* attempt to write. Therefore we keep a copy of the last
* message sent such that we can repeat it after an error.
*
* Note that this is imperfect since their can be multiple
* messages in flight at the point a connection collapses
* which will then be lost. This only handles the delayed
* notification of such an error to this code.
*/
lastMessage []byte
)
/* rfc5425 like scheme, see section 4.3 */
func rfc5425Write(conn vConn, buf []byte) error {
msglen := fmt.Sprintf("%d ", len(buf))
_, err := conn.Write([]byte(msglen))
/* XXX todo, check for serious vs retriable errors */
if err != nil {
console.Printf("Error in length write: %s", err)
return err
}
_, err = conn.Write(buf)
/* XXX todo, check for serious vs retriable errors */
if err != nil {
console.Printf("Error in buf write: %s", err)
}
return err
}
func forwardSyslogDatagram(buf []byte, portstr string) error {
for try := 0; try < 2; try++ {
conn := currentConn
if conn == nil {
if strings.Contains(portstr, "-") {
svcid, err := hvsock.GUIDFromString(portstr)
if err != nil {
console.Fatalln("Failed to parse GUID", portstr, err)
}
conn, err = hvsock.Dial(hvsock.HypervAddr{VMID: hvsock.GUIDWildcard, ServiceID: svcid})
if err != nil {
console.Printf("Failed to dial hvsock port: %s", err)
continue
}
} else {
port, err := strconv.ParseUint(portstr, 10, 32)
if err != nil {
console.Fatalln("Can't convert %s to a uint.", portstr, err)
}
conn, err = vsock.Dial(vsock.CIDHost, uint32(port))
if err != nil {
console.Printf("Failed to dial vsock port %d: %s", port, err)
continue
}
}
conn.CloseRead()
/*
* Only log on reconnection, not the initial connection since
* that is mostly uninteresting
*/
if alreadyConnectedOnce {
console.Printf("Opened new conn to %s: %#v", portstr, conn)
}
alreadyConnectedOnce = true
if lastMessage != nil {
console.Printf("Replaying last message: %s", lastMessage)
err := rfc5425Write(conn, lastMessage)
if err != nil {
conn.Close()
continue
}
lastMessage = nil
}
currentConn = conn
}
err := rfc5425Write(conn, buf)
if err != nil {
currentConn.Close()
currentConn = nil
console.Printf("Failed to write: %s", string(buf))
continue
}
if try > 0 {
console.Printf("Forwarded on attempt %d: %s", try+1, string(buf))
}
// Keep a copy in case we get an EPIPE from the next write
lastMessage = make([]byte, len(buf))
copy(lastMessage, buf)
return nil
}
lastMessage = nil // No point repeating this now
return errors.New("Failed to send datagram, too many retries")
}
func handleSyslogForward(cfg string) {
// logging to the default syslog while trying to do syslog
// forwarding would result in infinite loops, so log all
// messages in this callchain to the console instead.
logFile, err := os.OpenFile("/dev/console", os.O_WRONLY, 0)
if err != nil {
/* What are the chances of this going anywhere useful... */
log.Fatalln("Failed to open /dev/console for syslog forward logging", err)
}
console = log.New(logFile, "vsyslog: ", log.LstdFlags)
s := strings.SplitN(cfg, ":", 2)
if len(s) != 2 {
console.Fatalf("Failed to parse: %s", cfg)
}
vsock := s[0]
usock := s[1]
err = os.Remove(usock)
if err != nil && !os.IsNotExist(err) {
console.Printf("Failed to remove %s: %s", usock, err)
/* Try and carry on... */
}
l, err := net.ListenUnixgram("unixgram", &net.UnixAddr{Name: usock, Net: "unixgram"})
if err != nil {
console.Fatalf("Failed to listen to unixgram:%s: %s", usock, err)
}
var buf [4096]byte // Ugh, no way to peek at the next message's size in Go
for {
r, err := l.Read(buf[:])
if err != nil {
console.Fatalf("Failed to read: %s", err)
}
err = forwardSyslogDatagram(buf[:r], vsock)
if err != nil {
console.Printf("Failed to send log: %s: %s",
err, string(buf[:r]))
}
}
}
|