github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/service/common/conf.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package common 5 6 import ( 7 "reflect" 8 "strconv" 9 "strings" 10 11 "github.com/juju/errors" 12 "github.com/juju/utils/v3/shell" 13 ) 14 15 // Conf is responsible for defining services. Its fields 16 // represent elements of a service configuration. 17 type Conf struct { 18 // Desc is the init service's description. 19 Desc string 20 21 // Transient indicates whether or not the service is a one-off. 22 Transient bool 23 24 // AfterStopped is the name, if any, of another service. This 25 // service will not start until after the other stops. 26 AfterStopped string 27 28 // Env holds the environment variables that will be set when the 29 // command runs. 30 Env map[string]string 31 32 // TODO(ericsnow) Add a Limit type, since the possible keys are known. 33 34 // Limit holds the ulimit values that will be set when the command 35 // runs. Each value will be used as both the soft and hard limit. 36 // Valid values are integers or "infinity" 37 Limit map[string]string 38 39 // Timeout is how many seconds may pass before an exec call (e.g. 40 // ExecStart) times out. Values less than or equal to 0 (the 41 // default) are treated as though there is no timeout. 42 Timeout int 43 44 // ExecStart is the command (with arguments) that will be run. The 45 // path to the executable must be absolute. 46 // The command will be restarted if it exits with a non-zero exit code. 47 ExecStart string 48 49 // ExecStopPost is the command that will be run after the service stops. 50 // The path to the executable must be absolute. 51 ExecStopPost string 52 53 // Logfile, if set, indicates where the service's output should be 54 // written. 55 Logfile string 56 57 // TODO(ericsnow) Turn ExtraScript into ExecStartPre. 58 59 // ExtraScript allows to insert script before command execution. 60 ExtraScript string 61 62 // ServiceBinary is the actual binary without any arguments. 63 ServiceBinary string 64 65 // ServiceArgs is a string array of unquoted arguments 66 ServiceArgs []string 67 } 68 69 // IsZero determines whether or not the conf is a zero value. 70 func (c Conf) IsZero() bool { 71 return reflect.DeepEqual(c, Conf{}) 72 } 73 74 // Validate checks the conf's values for correctness. 75 func (c Conf) Validate(renderer shell.Renderer) error { 76 if c.Desc == "" { 77 return errors.New("missing Desc") 78 } 79 80 // Check the Exec* fields. 81 if c.ExecStart == "" { 82 return errors.New("missing ExecStart") 83 } 84 85 for field, cmd := range map[string]string{ 86 "ExecStart": c.ExecStart, 87 "ExecStopPost": c.ExecStopPost, 88 } { 89 if cmd == "" { 90 continue 91 } 92 if err := c.checkExec(field, cmd, renderer); err != nil { 93 return errors.Trace(err) 94 } 95 } 96 97 for _, value := range c.Limit { 98 switch value { 99 case "unlimited", "infinity": 100 default: 101 _, err := strconv.Atoi(value) 102 if err != nil { 103 newErr := errors.NotValidf("limit must be \"infinity\", \"unlimited\", or an integer, %q", value) 104 return errors.Wrap(err, newErr) 105 } 106 } 107 } 108 109 return nil 110 } 111 112 func (c Conf) checkExec(name, cmd string, renderer shell.Renderer) error { 113 executable := strings.Fields(cmd)[0] 114 executable = Unquote(executable) 115 if !renderer.IsAbs(executable) { 116 return errors.NotValidf("relative path in %s (%s)", name, executable) 117 } 118 return nil 119 } 120 121 // Unquote returns the string embedded between matching quotation marks. 122 // If there aren't any matching quotation marks then the string is 123 // returned as-is. 124 func Unquote(str string) string { 125 if len(str) < 2 { 126 return str 127 } 128 for _, quote := range []string{`'`, `"`} { 129 if str[0:1] == quote && str[len(str)-1:] == quote { 130 return strings.Trim(str, quote) 131 } 132 } 133 return str 134 }