1
// Copyright 2012, 2013 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
18
var startedRE = regexp.MustCompile(`^.* start/running, process (\d+)\n$`)
20
// Service provides visibility into and control over an upstart service.
23
InitDir string // defaults to "/etc/init"
26
func NewService(name string) *Service {
27
return &Service{Name: name, InitDir: "/etc/init"}
30
// confPath returns the path to the service's configuration file.
31
func (s *Service) confPath() string {
32
return path.Join(s.InitDir, s.Name+".conf")
35
// Installed returns whether the service configuration exists in the
37
func (s *Service) Installed() bool {
38
_, err := os.Stat(s.confPath())
42
// Running returns true if the Service appears to be running.
43
func (s *Service) Running() bool {
44
cmd := exec.Command("status", s.Name)
45
out, err := cmd.CombinedOutput()
49
return startedRE.Match(out)
52
// Start starts the service.
53
func (s *Service) Start() error {
57
return runCommand("start", s.Name)
60
func runCommand(args ...string) error {
61
out, err := exec.Command(args[0], args[1:]...).CombinedOutput()
65
out = bytes.TrimSpace(out)
67
return fmt.Errorf("exec %q: %v (%s)", args, err, out)
69
return fmt.Errorf("exec %q: %v", args, err)
72
// Stop stops the service.
73
func (s *Service) Stop() error {
77
return runCommand("stop", s.Name)
80
// Remove deletes the service configuration from the init directory.
81
func (s *Service) Remove() error {
85
if err := s.Stop(); err != nil {
88
return os.Remove(s.confPath())
91
// BUG: %q quoting does not necessarily match libnih quoting rules
92
// (as used by upstart); this may become an issue in the future.
93
var confT = template.Must(template.New("").Parse(`
94
description "{{.Desc}}"
95
author "Juju Team <juju@lists.ubuntu.com>"
96
start on runlevel [2345]
97
stop on runlevel [!2345]
100
{{range $k, $v := .Env}}env {{$k}}={{$v|printf "%q"}}
102
{{range $k, $v := .Limit}}limit {{$k}} {{$v}}
104
exec {{.Cmd}}{{if .Out}} >> {{.Out}} 2>&1{{end}}
107
// Conf is responsible for defining and installing upstart services. Its fields
108
// represent elements of an upstart service configuration file.
111
// Desc is the upstart service's description.
113
// Env holds the environment variables that will be set when the command runs.
114
Env map[string]string
115
// Limit holds the ulimit values that will be set when the command runs.
116
Limit map[string]string
117
// Cmd is the command (with arguments) that will be run.
118
// The command will be restarted if it exits with a non-zero exit code.
120
// Out, if set, will redirect output to that path.
124
// validate returns an error if the service is not adequately defined.
125
func (c *Conf) validate() error {
127
return errors.New("missing Name")
130
return errors.New("missing InitDir")
133
return errors.New("missing Desc")
136
return errors.New("missing Cmd")
141
// render returns the upstart configuration for the service as a string.
142
func (c *Conf) render() ([]byte, error) {
143
if err := c.validate(); err != nil {
147
if err := confT.Execute(&buf, c); err != nil {
150
return buf.Bytes(), nil
153
// Install installs and starts the service.
154
func (c *Conf) Install() error {
155
conf, err := c.render()
160
if err := c.Remove(); err != nil {
161
return fmt.Errorf("upstart: could not remove installed service: %s", err)
165
if err := ioutil.WriteFile(c.confPath(), conf, 0644); err != nil {
171
// InstallCommands returns shell commands to install and start the service.
172
func (c *Conf) InstallCommands() ([]string, error) {
173
conf, err := c.render()
178
fmt.Sprintf("cat >> %s << 'EOF'\n%sEOF\n", c.confPath(), conf),