package manager import ( "bytes" "fmt" "io/ioutil" "os" "path/filepath" "text/template" "git.rootprojects.org/root/go-serviceman/manager/static" "git.rootprojects.org/root/go-serviceman/service" ) var ( srvLen int srvExt = ".service" srvSysPath = "/etc/systemd/system" // Not sure which of these it's supposed to be... // * ~/.local/share/systemd/user/watchdog.service // * ~/.config/systemd/user/watchdog.service // https://wiki.archlinux.org/index.php/Systemd/User // This seems to work on Ubuntu srvUserPath = ".config/systemd/user" ) func init() { srvLen = len(srvExt) } func start(conf *service.Service) error { system := conf.System home := conf.Home name := conf.ReverseDNS _, err := getService(system, home, name) if nil != err { return err } var cmds []Runnable if system { cmds = []Runnable{ Runnable{ Exec: "systemctl", Args: []string{"daemon-reload"}, Must: false, }, Runnable{ Exec: "systemctl", Args: []string{"stop", name + ".service"}, Must: false, }, Runnable{ Exec: "systemctl", Args: []string{"enable", name + ".service"}, Badwords: []string{"not found", "failed"}, Must: true, }, Runnable{ Exec: "systemctl", Args: []string{"start", name + ".service"}, Badwords: []string{"not found", "failed"}, Must: true, }, } } else { cmds = []Runnable{ Runnable{ Exec: "systemctl", Args: []string{"--user", "daemon-reload"}, Must: false, }, Runnable{ Exec: "systemctl", Args: []string{"stop", "--user", name + ".service"}, Must: false, }, Runnable{ Exec: "systemctl", Args: []string{"start", "--user", name + ".service"}, Badwords: []string{"not found", "failed"}, Must: true, }, } } cmds = adjustPrivs(system, cmds) typ := "USER MODE" if system { typ = "SYSTEM" } fmt.Printf("Starting systemd %s service unit...\n\n", typ) for i := range cmds { exe := cmds[i] fmt.Println("\t" + exe.String()) err := exe.Run() if nil != err { return err } } fmt.Println() return nil } func stop(conf *service.Service) error { system := conf.System home := conf.Home name := conf.ReverseDNS _, err := getService(system, home, name) if nil != err { return err } var cmds []Runnable badwords := []string{"Failed to stop"} if system { cmds = []Runnable{ Runnable{ Exec: "systemctl", Args: []string{"stop", name + ".service"}, Must: true, Badwords: badwords, }, } } else { cmds = []Runnable{ Runnable{ Exec: "systemctl", Args: []string{"stop", "--user", name + ".service"}, Must: true, Badwords: badwords, }, } } cmds = adjustPrivs(system, cmds) fmt.Println() typ := "USER MODE" if system { typ = "SYSTEM" } fmt.Printf("Stopping systemd %s service...\n", typ) for i := range cmds { exe := cmds[i] fmt.Println("\t" + exe.String()) err := exe.Run() if nil != err { return err } } fmt.Println() return nil } // Render will create a systemd .service file using a simple the internal template func Render(c *service.Service) ([]byte, error) { defaultUserGroup(c) // Create service file from template b, err := static.ReadFile("dist/etc/systemd/system/_name_.service.tmpl") if err != nil { return nil, err } s := string(b) rw := &bytes.Buffer{} // not sure what the template name does, but whatever tmpl, err := template.New("service").Parse(s) if err != nil { return nil, err } err = tmpl.Execute(rw, c) if nil != err { return nil, err } return rw.Bytes(), nil } func install(c *service.Service) (string, error) { defaultUserGroup(c) // Check paths first serviceDir := srvSysPath if !c.System { serviceDir = filepath.Join(c.Home, srvUserPath) err := os.MkdirAll(serviceDir, 0755) if nil != err { return "", err } } b, err := Render(c) if nil != err { return "", err } // Write the file out serviceName := c.Name + ".service" servicePath := filepath.Join(serviceDir, serviceName) if err := ioutil.WriteFile(servicePath, b, 0644); err != nil { return "", fmt.Errorf("Error writing %s: %v", servicePath, err) } // TODO --no-start err = start(c) if nil != err { sudo := "" // --user-unit rather than --user --unit for older systemd unit := "--user-unit" if c.System { sudo = "sudo " unit = "--unit" } fmt.Printf("If things don't go well you should be able to get additional logging from journalctl:\n") fmt.Printf("\t%sjournalctl -xe %s %s.service\n", sudo, unit, c.Name) return "", err } return "systemd", nil } func defaultUserGroup(c *service.Service) { // Linux-specific config options if c.System { if "" == c.User { c.User = "root" } } if "" == c.Group { c.Group = c.User } }