2014-03-18 20:00:41 +04:00
|
|
|
package system
|
2014-03-05 04:36:05 +04:00
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2014-03-18 20:36:31 +04:00
|
|
|
"io/ioutil"
|
2014-03-05 04:36:05 +04:00
|
|
|
"log"
|
2014-03-13 02:43:51 +04:00
|
|
|
"os"
|
2014-03-13 09:30:24 +04:00
|
|
|
"os/exec"
|
2014-03-05 04:36:05 +04:00
|
|
|
"path"
|
2014-03-13 02:43:51 +04:00
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
2014-03-05 04:36:05 +04:00
|
|
|
|
2014-03-13 06:36:31 +04:00
|
|
|
"github.com/coreos/coreos-cloudinit/third_party/github.com/coreos/go-systemd/dbus"
|
2014-03-05 04:36:05 +04:00
|
|
|
)
|
|
|
|
|
2014-03-18 21:57:10 +04:00
|
|
|
// fakeMachineID is placed on non-usr CoreOS images and should
|
|
|
|
// never be used as a true MachineID
|
|
|
|
const fakeMachineID = "42000000000000000000000000000042"
|
|
|
|
|
2014-05-10 07:33:34 +04:00
|
|
|
// Name for drop-in service configuration files created by cloudconfig
|
|
|
|
const cloudConfigDropIn = "20-cloudinit.conf"
|
|
|
|
|
2014-03-13 02:43:51 +04:00
|
|
|
type Unit struct {
|
|
|
|
Name string
|
2014-05-10 07:33:34 +04:00
|
|
|
Mask bool
|
2014-04-14 21:02:18 +04:00
|
|
|
Enable bool
|
2014-03-13 02:43:51 +04:00
|
|
|
Runtime bool
|
|
|
|
Content string
|
2014-03-20 02:52:24 +04:00
|
|
|
Command string
|
2014-05-13 02:20:08 +04:00
|
|
|
|
|
|
|
// For drop-in units, a cloudinit.conf is generated.
|
|
|
|
// This is currently unbound in YAML (and hence unsettable in cloud-config files)
|
|
|
|
// until the correct behaviour for multiple drop-in units is determined.
|
|
|
|
DropIn bool `yaml:"-"`
|
2014-03-13 02:43:51 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
func (u *Unit) Type() string {
|
|
|
|
ext := filepath.Ext(u.Name)
|
|
|
|
return strings.TrimLeft(ext, ".")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (u *Unit) Group() (group string) {
|
|
|
|
t := u.Type()
|
|
|
|
if t == "network" || t == "netdev" || t == "link" {
|
|
|
|
group = "network"
|
|
|
|
} else {
|
|
|
|
group = "system"
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2014-03-05 04:36:05 +04:00
|
|
|
type Script []byte
|
|
|
|
|
2014-05-10 07:33:34 +04:00
|
|
|
// UnitDestination builds the appropriate absolute file path for
|
|
|
|
// the given Unit. The root argument indicates the effective base
|
2014-04-14 21:10:10 +04:00
|
|
|
// directory of the system (similar to a chroot).
|
|
|
|
func UnitDestination(u *Unit, root string) string {
|
2014-03-13 02:43:51 +04:00
|
|
|
dir := "etc"
|
|
|
|
if u.Runtime {
|
|
|
|
dir = "run"
|
|
|
|
}
|
|
|
|
|
2014-05-10 07:33:34 +04:00
|
|
|
if u.DropIn {
|
|
|
|
return path.Join(root, dir, "systemd", u.Group(), fmt.Sprintf("%s.d", u.Name), cloudConfigDropIn)
|
|
|
|
} else {
|
|
|
|
return path.Join(root, dir, "systemd", u.Group(), u.Name)
|
|
|
|
}
|
2014-04-14 21:10:10 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
// PlaceUnit writes a unit file at the provided destination, creating
|
|
|
|
// parent directories as necessary.
|
|
|
|
func PlaceUnit(u *Unit, dst string) error {
|
|
|
|
dir := filepath.Dir(dst)
|
|
|
|
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
|
|
|
if err := os.MkdirAll(dir, os.FileMode(0755)); err != nil {
|
|
|
|
return err
|
2014-03-13 02:43:51 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-03-18 20:00:41 +04:00
|
|
|
file := File{
|
2014-05-07 03:11:26 +04:00
|
|
|
Path: dst,
|
|
|
|
Content: u.Content,
|
2014-03-18 20:00:41 +04:00
|
|
|
RawFilePermissions: "0644",
|
|
|
|
}
|
|
|
|
|
|
|
|
err := WriteFile(&file)
|
2014-03-13 02:43:51 +04:00
|
|
|
if err != nil {
|
2014-04-14 21:10:10 +04:00
|
|
|
return err
|
2014-03-13 02:43:51 +04:00
|
|
|
}
|
|
|
|
|
2014-04-14 21:10:10 +04:00
|
|
|
return nil
|
2014-03-13 02:43:51 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
func EnableUnitFile(file string, runtime bool) error {
|
|
|
|
conn, err := dbus.New()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
files := []string{file}
|
|
|
|
_, _, err = conn.EnableUnitFiles(files, runtime, true)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2014-03-20 02:52:24 +04:00
|
|
|
func RunUnitCommand(command, unit string) (string, error) {
|
|
|
|
conn, err := dbus.New()
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
2014-03-13 02:43:51 +04:00
|
|
|
}
|
|
|
|
|
2014-03-20 02:52:24 +04:00
|
|
|
var fn func(string, string) (string, error)
|
|
|
|
switch command {
|
|
|
|
case "start":
|
|
|
|
fn = conn.StartUnit
|
|
|
|
case "stop":
|
|
|
|
fn = conn.StopUnit
|
|
|
|
case "restart":
|
|
|
|
fn = conn.RestartUnit
|
|
|
|
case "reload":
|
|
|
|
fn = conn.ReloadUnit
|
|
|
|
case "try-restart":
|
|
|
|
fn = conn.TryRestartUnit
|
|
|
|
case "reload-or-restart":
|
|
|
|
fn = conn.ReloadOrRestartUnit
|
|
|
|
case "reload-or-try-restart":
|
|
|
|
fn = conn.ReloadOrTryRestartUnit
|
|
|
|
default:
|
|
|
|
return "", fmt.Errorf("Unsupported systemd command %q", command)
|
2014-03-13 02:43:51 +04:00
|
|
|
}
|
|
|
|
|
2014-03-20 02:52:24 +04:00
|
|
|
return fn(unit, "replace")
|
2014-03-13 02:43:51 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
func DaemonReload() error {
|
|
|
|
conn, err := dbus.New()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2014-03-26 05:50:38 +04:00
|
|
|
return conn.Reload()
|
2014-03-13 02:43:51 +04:00
|
|
|
}
|
|
|
|
|
2014-03-05 09:02:20 +04:00
|
|
|
func ExecuteScript(scriptPath string) (string, error) {
|
2014-03-05 04:36:05 +04:00
|
|
|
props := []dbus.Property{
|
2014-03-05 05:46:35 +04:00
|
|
|
dbus.PropDescription("Unit generated and executed by coreos-cloudinit on behalf of user"),
|
2014-03-05 04:36:05 +04:00
|
|
|
dbus.PropExecStart([]string{"/bin/bash", scriptPath}, false),
|
|
|
|
}
|
|
|
|
|
|
|
|
base := path.Base(scriptPath)
|
2014-03-05 05:46:35 +04:00
|
|
|
name := fmt.Sprintf("coreos-cloudinit-%s.service", base)
|
2014-03-05 04:36:05 +04:00
|
|
|
|
|
|
|
log.Printf("Creating transient systemd unit '%s'", name)
|
|
|
|
|
|
|
|
conn, err := dbus.New()
|
|
|
|
if err != nil {
|
2014-03-05 09:02:20 +04:00
|
|
|
return "", err
|
2014-03-05 04:36:05 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
_, err = conn.StartTransientUnit(name, "replace", props...)
|
2014-03-05 09:02:20 +04:00
|
|
|
return name, err
|
2014-03-05 04:36:05 +04:00
|
|
|
}
|
2014-03-13 09:30:24 +04:00
|
|
|
|
|
|
|
func SetHostname(hostname string) error {
|
|
|
|
return exec.Command("hostnamectl", "set-hostname", hostname).Run()
|
|
|
|
}
|
2014-03-18 20:36:31 +04:00
|
|
|
|
2014-03-18 21:57:10 +04:00
|
|
|
func Hostname() (string, error) {
|
2014-03-18 22:04:33 +04:00
|
|
|
return os.Hostname()
|
2014-03-18 21:57:10 +04:00
|
|
|
}
|
|
|
|
|
2014-03-18 20:36:31 +04:00
|
|
|
func MachineID(root string) string {
|
|
|
|
contents, _ := ioutil.ReadFile(path.Join(root, "etc", "machine-id"))
|
2014-03-18 21:57:10 +04:00
|
|
|
id := strings.TrimSpace(string(contents))
|
|
|
|
|
|
|
|
if id == fakeMachineID {
|
|
|
|
id = ""
|
|
|
|
}
|
|
|
|
|
|
|
|
return id
|
2014-03-18 20:36:31 +04:00
|
|
|
}
|
2014-05-07 03:18:36 +04:00
|
|
|
|
|
|
|
func MaskUnit(unit string, root string) error {
|
|
|
|
masked := path.Join(root, "etc", "systemd", "system", unit)
|
|
|
|
if err := os.MkdirAll(path.Dir(masked), os.FileMode(0755)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return os.Symlink("/dev/null", masked)
|
|
|
|
}
|