1
// Copyright 2015 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
10
"github.com/juju/errors"
11
"github.com/juju/utils/featureflag"
12
"github.com/juju/utils/os"
13
"github.com/juju/utils/series"
14
"github.com/juju/utils/shell"
16
"github.com/juju/juju/feature"
17
"github.com/juju/juju/service/common"
18
"github.com/juju/juju/service/systemd"
19
"github.com/juju/juju/service/upstart"
20
"github.com/juju/juju/service/windows"
23
// DiscoverService returns an interface to a service appropriate
24
// for the current system
25
func DiscoverService(name string, conf common.Conf) (Service, error) {
26
initName, err := discoverInitSystem()
28
return nil, errors.Trace(err)
31
service, err := newService(name, conf, initName, series.HostSeries())
33
return nil, errors.Trace(err)
38
func discoverInitSystem() (string, error) {
39
initName, err := discoverLocalInitSystem()
40
if errors.IsNotFound(err) {
41
// Fall back to checking the juju version.
42
versionInitName, err2 := VersionInitSystem(series.HostSeries())
44
// The key error is the one from discoverLocalInitSystem so
45
// that is what we return.
46
return "", errors.Wrap(err2, err)
48
initName = versionInitName
49
} else if err != nil {
50
return "", errors.Trace(err)
55
// VersionInitSystem returns an init system name based on the provided
56
// series. If one cannot be identified a NotFound error is returned.
57
func VersionInitSystem(series string) (string, error) {
58
initName, err := versionInitSystem(series)
60
return "", errors.Trace(err)
62
logger.Debugf("discovered init system %q from series %q", initName, series)
66
func versionInitSystem(ser string) (string, error) {
67
seriesos, err := series.GetOSFromSeries(ser)
69
notFound := errors.NotFoundf("init system for series %q", ser)
70
return "", errors.Wrap(err, notFound)
75
return InitSystemWindows, nil
78
case "precise", "quantal", "raring", "saucy", "trusty", "utopic":
79
return InitSystemUpstart, nil
82
if featureflag.Enabled(feature.LegacyUpstart) {
83
return InitSystemUpstart, nil
85
return InitSystemSystemd, nil
88
return InitSystemSystemd, nil
90
return "", errors.NotFoundf("unknown os %q (from series %q), init system", seriesos, ser)
93
type discoveryCheck struct {
95
isRunning func() (bool, error)
98
var discoveryFuncs = []discoveryCheck{
99
{InitSystemUpstart, upstart.IsRunning},
100
{InitSystemSystemd, systemd.IsRunning},
101
{InitSystemWindows, windows.IsRunning},
104
func discoverLocalInitSystem() (string, error) {
105
for _, check := range discoveryFuncs {
106
local, err := check.isRunning()
108
logger.Debugf("failed to find init system %q: %v", check.name, err)
110
// We expect that in error cases "local" will be false.
112
logger.Debugf("discovered init system %q from local host", check.name)
113
return check.name, nil
116
return "", errors.NotFoundf("init system (based on local host)")
119
const discoverInitSystemScript = `
120
# Use guaranteed discovery mechanisms for known init systems.
121
if [ -d /run/systemd/system ]; then
124
elif [ -f /sbin/initctl ] && /sbin/initctl --system list 2>&1 > /dev/null; then
133
// DiscoverInitSystemScript returns the shell script to use when
134
// discovering the local init system. The script is quite specific to
135
// bash, so it includes an explicit bash shbang.
136
func DiscoverInitSystemScript() string {
137
renderer := shell.BashRenderer{}
138
data := renderer.RenderScript([]string{discoverInitSystemScript})
142
// shellCase is the template for a bash case statement, for use in
143
// newShellSelectCommand.
152
// newShellSelectCommand creates a bash case statement with clause for
153
// each of the linux init systems. The body of each clause comes from
154
// calling the provided handler with the init system name. If the
155
// handler does not support the args then it returns a false "ok" value.
156
func newShellSelectCommand(envVarName, dflt string, handler func(string) (string, bool)) string {
158
for _, initSystem := range linuxInitSystems {
159
cmd, ok := handler(initSystem)
163
cases = append(cases, initSystem+")", " "+cmd, " ;;")
169
return fmt.Sprintf(shellCase[1:], envVarName, strings.Join(cases, "\n"), dflt)