github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/service/systemd/cmdline.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package systemd 5 6 import ( 7 "fmt" 8 "os" 9 "strings" 10 11 "github.com/juju/errors" 12 "github.com/juju/utils/v3/exec" 13 "github.com/juju/utils/v3/shell" 14 ) 15 16 const executable = "/bin/systemctl" 17 18 type commands struct { 19 shell.BashRenderer 20 binary string 21 } 22 23 func (c commands) resolve(args string) string { 24 binary := c.binary 25 if binary == "" { 26 binary = executable 27 } 28 return binary + " " + args 29 } 30 31 func (c commands) unitFilename(name, dirname string) string { 32 return c.Join(dirname, name+".service") 33 } 34 35 func (c commands) listAll() string { 36 // We can't just use the same command as listRunning (with an extra 37 // "--all" because it misses some inactive units. 38 args := `list-unit-files --no-legend --no-page -l -t service` + 39 ` | grep -o -P '^\w[\S]*(?=\.service)'` 40 return c.resolve(args) 41 } 42 43 func (c commands) start(name string) string { 44 args := fmt.Sprintf("start %s.service", name) 45 return c.resolve(args) 46 } 47 48 func (c commands) link(name, dirname string) string { 49 filename := c.unitFilename(name, dirname) 50 args := fmt.Sprintf("link %s", c.Quote(filename)) 51 return c.resolve(args) 52 } 53 54 func (c commands) enableLinked(name, dirname string) string { 55 filename := c.unitFilename(name, dirname) 56 args := fmt.Sprintf("enable %s", c.Quote(filename)) 57 return c.resolve(args) 58 } 59 60 func (c commands) disable(name string) string { 61 args := fmt.Sprintf("disable %s.service", name) 62 return c.resolve(args) 63 } 64 65 func (c commands) reload() string { 66 args := "daemon-reload" 67 return c.resolve(args) 68 } 69 70 func (c commands) conf(name, dirname string) string { 71 serviceFile := c.unitFilename(name, dirname) 72 args := fmt.Sprintf("cat %s", serviceFile) 73 return args 74 } 75 76 func (c commands) writeConf(name, dirname string, data []byte) string { 77 filename := c.unitFilename(name, dirname) 78 cmds := c.WriteFile(filename, data) 79 return strings.Join(cmds, "\n") 80 } 81 82 func (c commands) writeFile(name, dirname string, data []byte) string { 83 filename := c.Join(dirname, name) 84 cmds := c.WriteFile(filename, data) 85 return strings.Join(cmds, "\n") 86 } 87 88 func (c commands) chmod(name, dirname string, perm os.FileMode) string { 89 filename := c.Join(dirname, name) 90 cmds := c.Chmod(filename, perm) 91 return strings.Join(cmds, "\n") 92 } 93 94 // Cmdline exposes the core operations of interacting with systemd units. 95 type Cmdline struct { 96 commands commands 97 } 98 99 // TODO(ericsnow) Support more commands (Status, Start, Install, Conf, etc.). 100 101 // ListAll returns the names of all enabled systemd units. 102 func (cl Cmdline) ListAll() ([]string, error) { 103 cmd := cl.commands.listAll() 104 105 out, err := cl.runCommand(cmd, "List") 106 if err != nil { 107 return nil, errors.Trace(err) 108 } 109 out = strings.TrimSpace(out) 110 111 if out == "" { 112 return nil, nil 113 } 114 return strings.Split(out, "\n"), nil 115 } 116 117 func (cl Cmdline) conf(name, dirname string) ([]byte, error) { 118 cmd := cl.commands.conf(name, dirname) 119 120 out, err := cl.runCommand(cmd, "get conf") 121 if err != nil { 122 return nil, errors.Trace(err) 123 } 124 out = strings.TrimSpace(out) 125 126 return []byte(out), nil 127 } 128 129 func (cl Cmdline) reload() error { 130 cmd := cl.commands.reload() 131 132 _, err := cl.runCommand(cmd, "") 133 if err != nil { 134 err = errors.Trace(err) 135 return err 136 } 137 138 return err 139 } 140 141 const runCommandMsg = "%s failed (%s)" 142 143 func (Cmdline) runCommand(cmd, label string) (string, error) { 144 resp, err := runCommands(exec.RunParams{ 145 Commands: cmd, 146 }) 147 if err != nil { 148 return "", errors.Annotatef(err, runCommandMsg, label, cmd) 149 } 150 out := string(resp.Stdout) 151 152 if resp.Code != 0 { 153 err := errors.Errorf( 154 "error executing %q: %s", 155 executable, 156 strings.Replace(string(resp.Stderr), "\n", "; ", -1), 157 ) 158 return out, errors.Annotatef(err, runCommandMsg, label, cmd) 159 } 160 return out, nil 161 } 162 163 var runCommands = func(args exec.RunParams) (*exec.ExecResponse, error) { 164 return exec.RunCommands(args) 165 }