// convert the servicebuilder yaml file into a runit service directory func (s *ServiceBuilder) stage(templates map[string]ServiceTemplate) error { for serviceName, template := range templates { stageDir := filepath.Join(s.StagingRoot, serviceName) // create the default log directory err := os.MkdirAll(filepath.Join(stageDir, "log"), 0755) if err != nil { return err } runScript, err := template.RunScript() if err != nil { return err } if _, err := util.WriteIfChanged(filepath.Join(stageDir, "run"), runScript, 0755); err != nil { return err } logScript, err := template.LogScript() if err != nil { return err } if _, err := util.WriteIfChanged(filepath.Join(stageDir, "log", "run"), logScript, 0755); err != nil { return err } } return nil }
// set the memory limit on a cgroup, 0 to unrestrict // https://www.kernel.org/doc/Documentation/cgroups/memory.txt func (subsys Subsystems) SetMemory(name string, bytes int) error { if subsys.Memory == "" { return UnsupportedError("memory") } softLimit := bytes hardLimit := 2 * bytes if hardLimit < softLimit { // Deal with overflow hardLimit = softLimit } if bytes == 0 { softLimit = -1 hardLimit = -1 } err := os.Mkdir(filepath.Join(subsys.Memory, name), 0755) if err != nil && !os.IsExist(err) { return err } // the hard memory limit must be set BEFORE the mem+swap limit // so we must clear the swap limit at the start err = ioutil.WriteFile(filepath.Join(subsys.Memory, name, "memory.memsw.limit_in_bytes"), []byte("-1\n"), 0) if err != nil { return err } _, err = util.WriteIfChanged(filepath.Join(subsys.Memory, name, "memory.soft_limit_in_bytes"), []byte(strconv.Itoa(softLimit)+"\n"), 0) if err != nil { return err } _, err = util.WriteIfChanged(filepath.Join(subsys.Memory, name, "memory.limit_in_bytes"), []byte(strconv.Itoa(hardLimit)+"\n"), 0) if err != nil { return err } _, err = util.WriteIfChanged(filepath.Join(subsys.Memory, name, "memory.memsw.limit_in_bytes"), []byte(strconv.Itoa(hardLimit)+"\n"), 0) if err != nil { return err } return nil }
// set the number of logical CPUs in a given cgroup, 0 to unrestrict // https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt func (subsys Subsystems) SetCPU(name string, cpus int) error { if subsys.CPU == "" { return UnsupportedError("cpu") } period := 1000000 // one million microseconds quota := cpus * period if cpus == 0 { // one hundred thousand microseconds is the default, -1 will return EINVAL period = 100000 // setting -1 here will unrestrict the cgroup, so the period won't matter quota = -1 } err := os.Mkdir(filepath.Join(subsys.CPU, name), 0755) if err != nil && !os.IsExist(err) { return err } _, err = util.WriteIfChanged( filepath.Join(subsys.CPU, name, "cpu.cfs_period_us"), []byte(strconv.Itoa(period)+"\n"), 0, ) if err != nil { return err } _, err = util.WriteIfChanged( filepath.Join(subsys.CPU, name, "cpu.cfs_quota_us"), []byte(strconv.Itoa(quota)+"\n"), 0, ) if err != nil { return err } return nil }
// convert the servicebuilder yaml file into a runit service directory func (s *ServiceBuilder) stage(templates map[string]ServiceTemplate, restartPolicy RestartPolicy) error { nobody, err := user.Lookup("nobody") if err != nil { return err } nobodyUid, err := strconv.ParseInt(nobody.Uid, 10, 64) if err != nil { return err } nobodyGid, err := strconv.ParseInt(nobody.Gid, 10, 64) if err != nil { return err } for serviceName, template := range templates { stageDir := filepath.Join(s.StagingRoot, serviceName) // create the default log directory logDir := filepath.Join(stageDir, "log") err := os.MkdirAll(logDir, 0755) if err != nil { return err } // create a place for the logs to go logMainDir := filepath.Join(logDir, "main") err = os.Mkdir(logMainDir, 0755) if err == nil { if !s.testingNoChown { err = os.Chown(logMainDir, int(nobodyUid), int(nobodyGid)) if err != nil { return err } } } else if !os.IsExist(err) { return err } runScript, err := template.runScript() if err != nil { return err } if _, err := util.WriteIfChanged(filepath.Join(stageDir, "run"), runScript, 0755); err != nil { return err } logScript, err := template.logScript() if err != nil { return err } if _, err := util.WriteIfChanged(filepath.Join(stageDir, "log", "run"), logScript, 0755); err != nil { return err } finishScript, err := template.finishScript() if err != nil { return err } if _, err := util.WriteIfChanged(filepath.Join(stageDir, "finish"), finishScript, 0755); err != nil { return err } // If a "down" file is not present, runit will restart the process // whenever it finishes. Prevent that if the requested restart policy // is not RestartAlways downPath := filepath.Join(stageDir, DOWN_FILE_NAME) if restartPolicy != RestartPolicyAlways { file, err := os.Create(downPath) if err != nil { return err } _ = file.Close() } else { // There could be a down file lying around from a // previous installation with a different restart // policy, check for it and remove it if present _, err := os.Stat(downPath) if err == nil { err := os.Remove(downPath) if err != nil { return util.Errorf("Unable to remove down file: %s", err) } } else if !os.IsNotExist(err) { return util.Errorf("Error checking for down file: %s", err) } } } return nil }