2014-03-18 09:00:41 -07:00
|
|
|
package system
|
2014-03-04 16:36:05 -08:00
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2014-03-18 09:36:31 -07:00
|
|
|
"io/ioutil"
|
2014-03-04 16:36:05 -08:00
|
|
|
"log"
|
2014-03-12 15:43:51 -07:00
|
|
|
"os"
|
2014-03-12 22:30:24 -07:00
|
|
|
"os/exec"
|
2014-03-04 16:36:05 -08:00
|
|
|
"path"
|
2014-03-12 15:43:51 -07:00
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
2014-03-04 16:36:05 -08:00
|
|
|
|
2014-09-21 19:22:27 -07:00
|
|
|
"github.com/coreos/coreos-cloudinit/config"
|
2014-03-12 19:36:31 -07:00
|
|
|
"github.com/coreos/coreos-cloudinit/third_party/github.com/coreos/go-systemd/dbus"
|
2014-03-04 16:36:05 -08:00
|
|
|
)
|
|
|
|
|
2014-06-05 17:40:53 -07:00
|
|
|
func NewUnitManager(root string) UnitManager {
|
|
|
|
return &systemd{root}
|
2014-03-12 15:43:51 -07:00
|
|
|
}
|
|
|
|
|
2014-06-05 17:40:53 -07:00
|
|
|
type systemd struct {
|
|
|
|
root string
|
2014-03-12 15:43:51 -07:00
|
|
|
}
|
|
|
|
|
2014-06-05 17:40:53 -07:00
|
|
|
// fakeMachineID is placed on non-usr CoreOS images and should
|
|
|
|
// never be used as a true MachineID
|
|
|
|
const fakeMachineID = "42000000000000000000000000000042"
|
2014-04-14 10:10:10 -07:00
|
|
|
|
|
|
|
// PlaceUnit writes a unit file at the provided destination, creating
|
|
|
|
// parent directories as necessary.
|
2014-06-05 17:40:53 -07:00
|
|
|
func (s *systemd) PlaceUnit(u *Unit, dst string) error {
|
2014-04-14 10:10:10 -07:00
|
|
|
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-12 15:43:51 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-21 19:22:27 -07:00
|
|
|
file := File{config.File{
|
2014-06-05 12:48:32 -07:00
|
|
|
Path: filepath.Base(dst),
|
2014-05-06 16:11:26 -07:00
|
|
|
Content: u.Content,
|
2014-03-18 09:00:41 -07:00
|
|
|
RawFilePermissions: "0644",
|
2014-09-21 19:22:27 -07:00
|
|
|
}}
|
2014-03-18 09:00:41 -07:00
|
|
|
|
2014-06-05 12:48:32 -07:00
|
|
|
_, err := WriteFile(&file, dir)
|
2014-03-12 15:43:51 -07:00
|
|
|
if err != nil {
|
2014-04-14 10:10:10 -07:00
|
|
|
return err
|
2014-03-12 15:43:51 -07:00
|
|
|
}
|
|
|
|
|
2014-04-14 10:10:10 -07:00
|
|
|
return nil
|
2014-03-12 15:43:51 -07:00
|
|
|
}
|
|
|
|
|
2014-06-05 17:40:53 -07:00
|
|
|
func (s *systemd) EnableUnitFile(unit string, runtime bool) error {
|
2014-03-12 15:43:51 -07:00
|
|
|
conn, err := dbus.New()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2014-05-26 15:16:24 -07:00
|
|
|
units := []string{unit}
|
|
|
|
_, _, err = conn.EnableUnitFiles(units, runtime, true)
|
2014-03-12 15:43:51 -07:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2014-06-05 17:40:53 -07:00
|
|
|
func (s *systemd) RunUnitCommand(command, unit string) (string, error) {
|
2014-03-19 15:52:24 -07:00
|
|
|
conn, err := dbus.New()
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
2014-03-12 15:43:51 -07:00
|
|
|
}
|
|
|
|
|
2014-03-19 15:52:24 -07: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-12 15:43:51 -07:00
|
|
|
}
|
|
|
|
|
2014-03-19 15:52:24 -07:00
|
|
|
return fn(unit, "replace")
|
2014-03-12 15:43:51 -07:00
|
|
|
}
|
|
|
|
|
2014-06-05 17:40:53 -07:00
|
|
|
func (s *systemd) DaemonReload() error {
|
2014-03-12 15:43:51 -07:00
|
|
|
conn, err := dbus.New()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2014-03-25 18:50:38 -07:00
|
|
|
return conn.Reload()
|
2014-03-12 15:43:51 -07:00
|
|
|
}
|
|
|
|
|
2014-06-03 16:49:26 -07:00
|
|
|
// MaskUnit masks the given Unit by symlinking its unit file to
|
|
|
|
// /dev/null, analogous to `systemctl mask`.
|
2014-05-21 11:03:52 -07:00
|
|
|
// N.B.: Unlike `systemctl mask`, this function will *remove any existing unit
|
2014-06-03 16:49:26 -07:00
|
|
|
// file at the location*, to ensure that the mask will succeed.
|
2014-06-05 17:40:53 -07:00
|
|
|
func (s *systemd) MaskUnit(unit *Unit) error {
|
|
|
|
masked := unit.Destination(s.root)
|
2014-05-21 11:03:52 -07:00
|
|
|
if _, err := os.Stat(masked); os.IsNotExist(err) {
|
|
|
|
if err := os.MkdirAll(path.Dir(masked), os.FileMode(0755)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else if err := os.Remove(masked); err != nil {
|
2014-05-06 16:18:36 -07:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
return os.Symlink("/dev/null", masked)
|
|
|
|
}
|
2014-06-03 16:49:26 -07:00
|
|
|
|
|
|
|
// UnmaskUnit is analogous to systemd's unit_file_unmask. If the file
|
|
|
|
// associated with the given Unit is empty or appears to be a symlink to
|
|
|
|
// /dev/null, it is removed.
|
2014-06-05 17:40:53 -07:00
|
|
|
func (s *systemd) UnmaskUnit(unit *Unit) error {
|
|
|
|
masked := unit.Destination(s.root)
|
2014-06-03 16:49:26 -07:00
|
|
|
ne, err := nullOrEmpty(masked)
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
return nil
|
|
|
|
} else if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if !ne {
|
|
|
|
log.Printf("%s is not null or empty, refusing to unmask", masked)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return os.Remove(masked)
|
|
|
|
}
|
|
|
|
|
|
|
|
// nullOrEmpty checks whether a given path appears to be an empty regular file
|
|
|
|
// or a symlink to /dev/null
|
|
|
|
func nullOrEmpty(path string) (bool, error) {
|
|
|
|
fi, err := os.Stat(path)
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
m := fi.Mode()
|
|
|
|
if m.IsRegular() && fi.Size() <= 0 {
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
if m&os.ModeCharDevice > 0 {
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
return false, nil
|
|
|
|
}
|
2014-06-05 17:40:53 -07:00
|
|
|
|
|
|
|
func ExecuteScript(scriptPath string) (string, error) {
|
|
|
|
props := []dbus.Property{
|
|
|
|
dbus.PropDescription("Unit generated and executed by coreos-cloudinit on behalf of user"),
|
|
|
|
dbus.PropExecStart([]string{"/bin/bash", scriptPath}, false),
|
|
|
|
}
|
|
|
|
|
|
|
|
base := path.Base(scriptPath)
|
|
|
|
name := fmt.Sprintf("coreos-cloudinit-%s.service", base)
|
|
|
|
|
|
|
|
log.Printf("Creating transient systemd unit '%s'", name)
|
|
|
|
|
|
|
|
conn, err := dbus.New()
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = conn.StartTransientUnit(name, "replace", props...)
|
|
|
|
return name, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func SetHostname(hostname string) error {
|
|
|
|
return exec.Command("hostnamectl", "set-hostname", hostname).Run()
|
|
|
|
}
|
|
|
|
|
|
|
|
func Hostname() (string, error) {
|
|
|
|
return os.Hostname()
|
|
|
|
}
|
|
|
|
|
|
|
|
func MachineID(root string) string {
|
|
|
|
contents, _ := ioutil.ReadFile(path.Join(root, "etc", "machine-id"))
|
|
|
|
id := strings.TrimSpace(string(contents))
|
|
|
|
|
|
|
|
if id == fakeMachineID {
|
|
|
|
id = ""
|
|
|
|
}
|
|
|
|
|
|
|
|
return id
|
|
|
|
}
|