-
Notifications
You must be signed in to change notification settings - Fork 42
/
process.go
119 lines (106 loc) · 3.21 KB
/
process.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// Package pexec defines process management utilities to be used as a library within
// a go process wishing to own sub-processes.
//
// It helps manage the lifecycle of processes by keeping them up as long as possible
// when configured.
package pexec
import (
"encoding/json"
"io"
"syscall"
"time"
"github.com/pkg/errors"
"go.viam.com/utils"
)
// defaultStopTimeout is how long to wait in seconds (all stages) between first signaling and finally killing.
const defaultStopTimeout = time.Second * 10
// A ProcessConfig describes how to manage a system process.
type ProcessConfig struct {
ID string
Name string
Args []string
CWD string
OneShot bool
Log bool
LogWriter io.Writer
StopSignal syscall.Signal
StopTimeout time.Duration
// OnUnexpectedExit will be called when the manage goroutine detects an
// unexpected exit of the process. The exit code of the crashed process will
// be passed in. If the returned bool is true, the manage goroutine will
// attempt to restart the process. Otherwise, the manage goroutine will
// simply return.
OnUnexpectedExit func(int) bool
}
// Validate ensures all parts of the config are valid.
func (config *ProcessConfig) Validate(path string) error {
if config.ID == "" {
return utils.NewConfigValidationFieldRequiredError(path, "id")
}
if config.Name == "" {
return utils.NewConfigValidationFieldRequiredError(path, "name")
}
if config.StopTimeout < 100*time.Millisecond && config.StopTimeout != 0 {
return utils.NewConfigValidationError(path, errors.New("stop_timeout should not be less than 100ms"))
}
return nil
}
// Note: keep this in sync with json-supported fields in ProcessConfig.
type configData struct {
ID string `json:"id"`
Name string `json:"name"`
Args []string `json:"args"`
CWD string `json:"cwd"`
OneShot bool `json:"one_shot"`
Log bool `json:"log"`
StopSignal string `json:"stop_signal,omitempty"`
StopTimeout string `json:"stop_timeout,omitempty"`
}
// UnmarshalJSON parses incoming json.
func (config *ProcessConfig) UnmarshalJSON(data []byte) error {
var temp configData
if err := json.Unmarshal(data, &temp); err != nil {
return err
}
*config = ProcessConfig{
ID: temp.ID,
Name: temp.Name,
Args: temp.Args,
CWD: temp.CWD,
OneShot: temp.OneShot,
Log: temp.Log,
// OnUnexpectedExit cannot be specified in JSON.
}
if temp.StopTimeout != "" {
dur, err := time.ParseDuration(temp.StopTimeout)
if err != nil {
return err
}
config.StopTimeout = dur
}
stopSig, err := parseSignal(temp.StopSignal, "stop_signal")
if err != nil {
return err
}
config.StopSignal = stopSig
return nil
}
// MarshalJSON converts to json.
func (config ProcessConfig) MarshalJSON() ([]byte, error) {
var stopSig string
if config.StopSignal != 0 {
stopSig = config.StopSignal.String()
}
temp := configData{
ID: config.ID,
Name: config.Name,
Args: config.Args,
CWD: config.CWD,
OneShot: config.OneShot,
Log: config.Log,
StopSignal: stopSig,
StopTimeout: config.StopTimeout.String(),
// OnUnexpectedExit cannot be converted to JSON.
}
return json.Marshal(temp)
}