// normalize adjusts the conf to more standardized content and // returns a new Conf with that updated content. It also returns the // content of any script file that should accompany the conf. func normalize(name string, conf common.Conf, scriptPath string, renderer confRenderer) (common.Conf, []byte) { var data []byte var cmds []string if conf.Logfile != "" { filename := conf.Logfile cmds = append(cmds, "# Set up logging.") cmds = append(cmds, renderer.Touch(filename, nil)...) // TODO(ericsnow) We should drop the assumption that the logfile // is syslog. user, group := syslogUserGroup() cmds = append(cmds, renderer.Chown(filename, user, group)...) cmds = append(cmds, renderer.Chmod(filename, 0600)...) cmds = append(cmds, renderer.RedirectOutput(filename)...) cmds = append(cmds, renderer.RedirectFD("out", "err")...) cmds = append(cmds, "", "# Run the script.", ) // We leave conf.Logfile alone (it will be ignored during validation). } cmds = append(cmds, conf.ExecStart) if conf.ExtraScript != "" { cmds = append([]string{conf.ExtraScript}, cmds...) conf.ExtraScript = "" } if !isSimpleCommand(strings.Join(cmds, "\n")) { data = renderer.RenderScript(cmds) conf.ExecStart = scriptPath } if len(conf.Env) == 0 { conf.Env = nil } if len(conf.Limit) == 0 { conf.Limit = nil } if conf.Transient { // TODO(ericsnow) Handle Transient via systemd-run command? conf.ExecStopPost = commands{}.disable(name) } return conf, data }
func deserializeOptions(opts []*unit.UnitOption, renderer shell.Renderer) (common.Conf, error) { var conf common.Conf for _, uo := range opts { switch uo.Section { case "Unit": switch uo.Name { case "Description": conf.Desc = uo.Value case "After": // Do nothing until we support it in common.Conf. default: return conf, errors.NotSupportedf("Unit directive %q", uo.Name) } case "Service": switch { case uo.Name == "ExecStart": conf.ExecStart = uo.Value case uo.Name == "Environment": if conf.Env == nil { conf.Env = make(map[string]string) } var value = uo.Value if strings.HasPrefix(value, `"`) && strings.HasSuffix(value, `"`) { value = value[1 : len(value)-1] } parts := strings.SplitN(value, "=", 2) if len(parts) != 2 { return conf, errors.NotValidf("service environment value %q", uo.Value) } conf.Env[parts[0]] = parts[1] case strings.HasPrefix(uo.Name, "Limit"): if conf.Limit == nil { conf.Limit = make(map[string]int) } for k, v := range limitMap { if v == uo.Name { n, err := strconv.Atoi(uo.Value) if err != nil { return conf, errors.Trace(err) } conf.Limit[k] = n break } } case uo.Name == "TimeoutSec": timeout, err := strconv.Atoi(uo.Value) if err != nil { return conf, errors.Trace(err) } conf.Timeout = timeout case uo.Name == "Type": // Do nothing until we support it in common.Conf. case uo.Name == "RemainAfterExit": // Do nothing until we support it in common.Conf. case uo.Name == "Restart": // Do nothing until we support it in common.Conf. default: return conf, errors.NotSupportedf("Service directive %q", uo.Name) } case "Install": switch uo.Name { case "WantedBy": if uo.Value != "multi-user.target" { return conf, errors.NotValidf("unit target %q", uo.Value) } default: return conf, errors.NotSupportedf("Install directive %q", uo.Name) } default: return conf, errors.NotSupportedf("section %q", uo.Name) } } err := validate("<>", conf, renderer) return conf, errors.Trace(err) }