github.com/fafucoder/cilium@v1.6.11/proxylib/accesslog/client.go (about) 1 // Copyright 2018 Authors of Cilium 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package accesslog 16 17 import ( 18 "net" 19 "sync/atomic" 20 "unsafe" 21 22 "github.com/cilium/cilium/pkg/lock" 23 "github.com/cilium/cilium/proxylib/proxylib" 24 25 "github.com/cilium/proxy/go/cilium/api" 26 "github.com/golang/protobuf/proto" 27 log "github.com/sirupsen/logrus" 28 ) 29 30 type Client struct { 31 connected uint32 // Accessed atomically without locking 32 path string 33 mutex lock.Mutex // Used to protect opening the connection 34 conn unsafe.Pointer // Read atomically without locking 35 } 36 37 func (cl *Client) connect() *net.UnixConn { 38 if cl.path == "" { 39 return nil 40 } 41 42 if atomic.LoadUint32(&cl.connected) > 0 { 43 // Guaranteed to be non-nil 44 return (*net.UnixConn)(atomic.LoadPointer(&cl.conn)) 45 } 46 47 cl.mutex.Lock() 48 defer cl.mutex.Unlock() 49 50 // Safe to read cl.conn while holding the mutex 51 conn := (*net.UnixConn)(cl.conn) 52 53 // Did someone else connect while we were contending on the lock? 54 // cl.connected may be written to by others concurrently 55 if atomic.LoadUint32(&cl.connected) > 0 { 56 return conn 57 } 58 59 if conn != nil { 60 conn.Close() // not setting conn to nil! 61 } 62 log.Debugf("Accesslog: Connecting to Cilium access log socket: %s", cl.path) 63 conn, err := net.DialUnix("unixpacket", nil, &net.UnixAddr{Name: cl.path, Net: "unixpacket"}) 64 if err != nil { 65 log.Errorf("Accesslog: DialUnix() failed: %v", err) 66 return nil 67 } 68 69 atomic.StorePointer(&cl.conn, unsafe.Pointer(conn)) 70 71 // Always have a non-nil 'cl.conn' after 'cl.connected' is set for the first time! 72 atomic.StoreUint32(&cl.connected, 1) 73 return conn 74 } 75 76 func (cl *Client) Log(pblog *cilium.LogEntry) { 77 if conn := cl.connect(); conn != nil { 78 // Encode 79 logmsg, err := proto.Marshal(pblog) 80 if err != nil { 81 log.Errorf("Accesslog: Protobuf marshaling error: %v", err) 82 return 83 } 84 85 // Write 86 _, err = conn.Write(logmsg) 87 if err != nil { 88 log.Errorf("Accesslog: Write() failed: %v", err) 89 atomic.StoreUint32(&cl.connected, 0) // Mark connection as broken 90 } 91 } else { 92 log.Debugf("Accesslog: No connection, cannot send: %s", pblog.String()) 93 } 94 } 95 96 func (c *Client) Path() string { 97 return c.path 98 } 99 100 func NewClient(accessLogPath string) proxylib.AccessLogger { 101 client := &Client{ 102 path: accessLogPath, 103 } 104 client.connect() 105 return client 106 } 107 108 func (cl *Client) Close() { 109 conn := (*net.UnixConn)(atomic.LoadPointer(&cl.conn)) 110 if conn != nil { 111 conn.Close() 112 } 113 }