mirror of
https://github.com/prometheus/node_exporter.git
synced 2024-12-22 16:36:07 +01:00
33f99c4fc1
Uses godep to vendor dependencies. Godeps is not necessary during build, golang's new vendor support is used instead.
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
|
|
}
|