Node_Exporter/vendor/github.com/soundcloud/go-runit/runit/runit.go

152 lines
2.7 KiB
Go
Raw Normal View History

package runit
import (
"errors"
"fmt"
"io/ioutil"
"os"
"syscall"
"time"
)
const (
defaultServiceDir = "/etc/service"
taiOffset = 4611686018427387914
statusLen = 20
posTimeStart = 0
posTimeEnd = 7
posPidStart = 12
posPidEnd = 15
posWant = 17
posState = 19
StateDown = 0
StateUp = 1
StateFinish = 2
)
var (
ENoRunsv = errors.New("runsv not running")
StateToString = map[int]string{
StateDown: "down",
StateUp: "up",
StateFinish: "finish",
}
)
type SvStatus struct {
Pid int
Duration int
Timestamp time.Time
State int
NormallyUp bool
Want int
}
type service struct {
Name string
ServiceDir string
}
func GetServices(dir string) ([]*service, error) {
if dir == "" {
dir = defaultServiceDir
}
files, err := ioutil.ReadDir(dir)
if err != nil {
return nil, err
}
services := []*service{}
for _, file := range files {
if file.Mode()&os.ModeSymlink == os.ModeSymlink || file.IsDir() {
services = append(services, GetService(file.Name(), dir))
}
}
return services, nil
}
func GetService(name string, dir string) *service {
if dir == "" {
dir = defaultServiceDir
}
r := service{Name: name, ServiceDir: dir}
return &r
}
func (s *service) file(file string) string {
return fmt.Sprintf("%s/%s/supervise/%s", s.ServiceDir, s.Name, file)
}
func (s *service) runsvRunning() (bool, error) {
file, err := os.OpenFile(s.file("ok"), os.O_WRONLY, 0)
if err != nil {
if err == syscall.ENXIO {
return false, nil
}
return false, err
}
file.Close()
return true, nil
}
func (s *service) status() ([]byte, error) {
file, err := os.Open(s.file("status"))
if err != nil {
return nil, err
}
defer file.Close()
status := make([]byte, statusLen)
_, err = file.Read(status)
return status, err
}
func (s *service) NormallyUp() bool {
_, err := os.Stat(s.file("down"))
return err != nil
}
func (s *service) Status() (*SvStatus, error) {
status, err := s.status()
if err != nil {
return nil, err
}
var pid int
pid = int(status[posPidEnd])
for i := posPidEnd - 1; i >= posPidStart; i-- {
pid <<= 8
pid += int(status[i])
}
tai := int64(status[posTimeStart])
for i := posTimeStart + 1; i <= posTimeEnd; i++ {
tai <<= 8
tai += int64(status[i])
}
state := status[posState] // 0: down, 1: run, 2: finish
tv := &syscall.Timeval{}
if err := syscall.Gettimeofday(tv); err != nil {
return nil, err
}
sS := SvStatus{
Pid: pid,
Timestamp: time.Unix(tai-taiOffset, 0), // FIXME: do we just select the wrong slice?
Duration: int(int64(tv.Sec) - (tai - taiOffset)),
State: int(state),
NormallyUp: s.NormallyUp(),
}
switch status[posWant] {
case 'u':
sS.Want = StateUp
case 'd':
sS.Want = StateDown
}
return &sS, nil
}