mirror of
https://github.com/prometheus/node_exporter.git
synced 2024-12-25 11:49:58 +01:00
152 lines
2.7 KiB
Go
152 lines
2.7 KiB
Go
|
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
|
||
|
}
|