Skip to content
This repository has been archived by the owner on Jan 28, 2021. It is now read-only.

Commit

Permalink
Added a checker that will detect dead sockets before the timeout (Lin…
Browse files Browse the repository at this point in the history
…ux only)

Signed-off-by: Juanjo Alvarez <juanjo@sourced.tech>
  • Loading branch information
Juanjo Alvarez committed Aug 16, 2019
1 parent e742bea commit e806bcd
Show file tree
Hide file tree
Showing 13 changed files with 768 additions and 191 deletions.
5 changes: 2 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module github.com/src-d/go-mysql-server

require (
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect
github.com/VividCortex/gohistogram v1.0.0 // indirect
github.com/go-kit/kit v0.8.0
github.com/go-ole/go-ole v1.2.4 // indirect
Expand All @@ -12,18 +13,16 @@ require (
github.com/mitchellh/hashstructure v1.0.0
github.com/oliveagle/jsonpath v0.0.0-20180606110733-2e52cf6e6852
github.com/opentracing/opentracing-go v1.0.2
github.com/pbnjay/memory v0.0.0-20190104145345-974d429e7ae4
github.com/pilosa/pilosa v1.3.0
github.com/sanity-io/litter v1.1.0
github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 // indirect
github.com/sirupsen/logrus v1.3.0
github.com/spf13/cast v1.3.0
github.com/src-d/go-oniguruma v1.0.0
github.com/stretchr/testify v1.3.0
go.etcd.io/bbolt v1.3.2
google.golang.org/grpc v1.19.0 // indirect
gopkg.in/src-d/go-errors.v1 v1.0.0
gopkg.in/src-d/go-git.v4 v4.13.1
gopkg.in/src-d/go-mysql-server.v0 v0.5.1
gopkg.in/yaml.v2 v2.2.2
vitess.io/vitess v3.0.0-rc.3.0.20190602171040-12bfde34629c+incompatible
)
93 changes: 5 additions & 88 deletions go.sum

Large diffs are not rendered by default.

82 changes: 82 additions & 0 deletions internal/sockstate/netstat.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package sockstate

import (
"fmt"
"net"
)

// OS independent part of the netstat_[OS].go modules
// Taken (simplified, privatized and with utility functions added) from:
// https://github.com/cakturk/go-netstat

// skState type represents socket connection state
type skState uint8

func (s skState) String() string {
return skStates[s]
}

// Socket states
const (
Established skState = 0x01
SynSent = 0x02
SynRecv = 0x03
FinWait1 = 0x04
FinWait2 = 0x05
TimeWait = 0x06
Close = 0x07
CloseWait = 0x08
LastAck = 0x09
Listen = 0x0a
Closing = 0x0b
)

var skStates = [...]string{
"UNKNOWN",
"ESTABLISHED",
"SYN_SENT",
"SYN_RECV",
"FIN_WAIT1",
"FIN_WAIT2",
"TIME_WAIT",
"", // CLOSE
"CLOSE_WAIT",
"LAST_ACK",
"LISTEN",
"CLOSING",
}

// sockAddr represents an ip:port pair
type sockAddr struct {
IP net.IP
Port uint16
}

func (s *sockAddr) String() string {
return fmt.Sprintf("%v:%d", s.IP, s.Port)
}

// sockTabEntry type represents each line of the /proc/net/tcp
type sockTabEntry struct {
Ino string
LocalAddr *sockAddr
RemoteAddr *sockAddr
State skState
UID uint32
Process *process
}

// process holds the PID and process name to which each socket belongs
type process struct {
pid int
name string
}

func (p *process) String() string {
return fmt.Sprintf("%d/%s", p.pid, p.name)
}

// AcceptFn is used to filter socket entries. The value returned indicates
// whether the element is to be appended to the socket list.
type AcceptFn func(*sockTabEntry) bool

11 changes: 11 additions & 0 deletions internal/sockstate/netstat_darwin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package sockstate

import "github.com/sirupsen/logrus"

// tcpSocks returns a slice of active TCP sockets containing only those
// elements that satisfy the accept function
func tcpSocks(accept AcceptFn) ([]sockTabEntry, error) {
// (juanjux) TODO: not implemented
logrus.Info("Connection checking not implemented for Darwin")
return []sockTabEntry{}, nil
}
206 changes: 206 additions & 0 deletions internal/sockstate/netstat_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
package sockstate

// Taken (simplified and with utility functions added) from https://github.com/cakturk/go-netstat

import (
"bufio"
"bytes"
"encoding/binary"
"fmt"
"io"
"io/ioutil"
"log"
"net"
"os"
"path"
"strconv"
"strings"
)

const (
pathTCPTab = "/proc/net/tcp"
ipv4StrLen = 8
)

type procFd struct {
base string
pid int
sktab []sockTabEntry
p *process
}

const sockPrefix = "socket:["

func getProcName(s []byte) string {
i := bytes.Index(s, []byte("("))
if i < 0 {
return ""
}
j := bytes.LastIndex(s, []byte(")"))
if i < 0 {
return ""
}
if i > j {
return ""
}
return string(s[i+1 : j])
}

func (p *procFd) iterFdDir() {
// link name is of the form socket:[5860846]
fddir := path.Join(p.base, "/fd")
fi, err := ioutil.ReadDir(fddir)
if err != nil {
return
}
var buf [128]byte

for _, file := range fi {
fd := path.Join(fddir, file.Name())
lname, err := os.Readlink(fd)
if err != nil {
continue
}

for i := range p.sktab {
sk := &p.sktab[i]
ss := sockPrefix + sk.Ino + "]"
if ss != lname {
continue
}
if p.p == nil {
stat, err := os.Open(path.Join(p.base, "stat"))
if err != nil {
return
}
n, err := stat.Read(buf[:])
_ = stat.Close()
if err != nil {
return
}
z := bytes.SplitN(buf[:n], []byte(" "), 3)
name := getProcName(z[1])
p.p = &process{p.pid, name}
}
sk.Process = p.p
}
}
}

func extractProcInfo(sktab []sockTabEntry) {
const basedir = "/proc"
fi, err := ioutil.ReadDir(basedir)
if err != nil {
return
}

for _, file := range fi {
if !file.IsDir() {
continue
}
pid, err := strconv.Atoi(file.Name())
if err != nil {
continue
}
base := path.Join(basedir, file.Name())
proc := procFd{base: base, pid: pid, sktab: sktab}
proc.iterFdDir()
}
}

func parseIPv4(s string) (net.IP, error) {
v, err := strconv.ParseUint(s, 16, 32)
if err != nil {
return nil, err
}
ip := make(net.IP, net.IPv4len)
binary.LittleEndian.PutUint32(ip, uint32(v))
return ip, nil
}

func parseAddr(s string) (*sockAddr, error) {
fields := strings.Split(s, ":")
if len(fields) < 2 {
return nil, fmt.Errorf("sockstate: not enough fields: %v", s)
}
var ip net.IP
var err error
switch len(fields[0]) {
case ipv4StrLen:
ip, err = parseIPv4(fields[0])
default:
log.Fatal("Bad formatted string")
}
if err != nil {
return nil, err
}
v, err := strconv.ParseUint(fields[1], 16, 16)
if err != nil {
return nil, err
}
return &sockAddr{IP: ip, Port: uint16(v)}, nil
}

func parseSocktab(r io.Reader, accept AcceptFn) ([]sockTabEntry, error) {
br := bufio.NewScanner(r)
tab := make([]sockTabEntry, 0, 4)

// Discard title
br.Scan()

for br.Scan() {
var e sockTabEntry
line := br.Text()
// Skip comments
if i := strings.Index(line, "#"); i >= 0 {
line = line[:i]
}
fields := strings.Fields(line)
if len(fields) < 12 {
return nil, fmt.Errorf("sockstate: not enough fields: %v, %v", len(fields), fields)
}
addr, err := parseAddr(fields[1])
if err != nil {
return nil, err
}
e.LocalAddr = addr
addr, err = parseAddr(fields[2])
if err != nil {
return nil, err
}
e.RemoteAddr = addr
u, err := strconv.ParseUint(fields[3], 16, 8)
if err != nil {
return nil, err
}
e.State = skState(u)
u, err = strconv.ParseUint(fields[7], 10, 32)
if err != nil {
return nil, err
}
e.UID = uint32(u)
e.Ino = fields[9]
if accept(&e) {
tab = append(tab, e)
}
}
return tab, br.Err()
}

// tcpSocks returns a slice of active TCP sockets containing only those
// elements that satisfy the accept function
func tcpSocks(accept AcceptFn) ([]sockTabEntry, error) {
f, err := os.Open(pathTCPTab)
defer f.Close()
if err != nil {
return nil, err
}

tabs, err := parseSocktab(f, accept)
if err != nil {
return nil, err
}

extractProcInfo(tabs)
return tabs, nil
}
11 changes: 11 additions & 0 deletions internal/sockstate/netstat_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package sockstate

import "github.com/sirupsen/logrus"

// tcpSocks returns a slice of active TCP sockets containing only those
// elements that satisfy the accept function
func tcpSocks(accept AcceptFn) ([]sockTabEntry, error) {
// (juanjux) TODO: not implemented
logrus.Info("Connection checking not implemented for Windows")
return []sockTabEntry{}, nil
}
Loading

0 comments on commit e806bcd

Please sign in to comment.