<commit_before>package main

import (
	"fmt"
	"gnd.la/config"
	"gnd.la/log"
	"gnd.la/util/textutil"
	"io/ioutil"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"syscall"
)

type Config struct {
	File             string
	Command          string
	Name             string
	Dir              string
	Environment      map[string]string
	User             string
	Group            string
	Priority         int `default:"1000"`
	Watchdog         *Watchdog
	WatchdogInterval int `default:"300"`
	Log              *Logger
	Err              error
}

func (c *Config) Cmd() (*exec.Cmd, error) {
	if c.Err != nil {
		return nil, c.Err
	}
	if c.Command == "" {
		return nil, fmt.Errorf("no command")
	}
	fields, err := textutil.SplitFields(c.Command, " ")
	if err != nil {
		return nil, err
	}
	if !filepath.IsAbs(fields[0]) {
		p, err := exec.LookPath(fields[0])
		if err != nil {
			return nil, err
		}
		fields[0] = p
	}
	dir := c.Dir
	if dir == "" {
		dir = filepath.Dir(fields[0])
	}
	cmd := &exec.Cmd{Path: fields[0], Args: fields, Dir: dir}
	for k, v := range c.Environment {
		cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", k, v))
	}
	if _, ok := c.Environment["GOMAXPROCS"]; !ok {
		cmd.Env = append(cmd.Env, fmt.Sprintf("GOMAXPROCS=%d", runtime.NumCPU()))
	}
	for _, v := range os.Environ() {
		if p := strings.IndexByte(v, '='); p >= 0 {
			k := v[:p]
			if _, ok := c.Environment[k]; !ok {
				cmd.Env = append(cmd.Env, v)
			}
		}
	}
	info, err := os.Stat(fields[0])
	if err != nil {
		return nil, err
	}
	stat := info.Sys().(*syscall.Stat_t)
	uid := stat.Uid
	gid := stat.Gid
	if c.Group != "" {
		if g := getGroupId(c.Group); g > 0 {
			gid = uint32(g)
		} else {
			return nil, fmt.Errorf("invalid group %q", c.Group)
		}
	}
	if c.User != "" {
		u, err := user.Lookup(c.User)
		if err != nil {
			return nil, err
		}
		ui, _ := strconv.Atoi(u.Uid)
		uid = uint32(ui)
		if gid == 0 {
			gi, _ := strconv.Atoi(u.Gid)
			gid = uint32(gi)
		}
	}
	var cred *syscall.Credential
	if uid != 0 || gid != 0 {
		cred = &syscall.Credential{Uid: uid, Gid: gid}
		attr := &syscall.SysProcAttr{Credential: cred}
		cmd.SysProcAttr = attr
	}
	log.Debugf("%s wd: %s, env: %s, cred: %+v", c.ServiceName(), dir, cmd.Env, cred)
	return cmd, nil
}

func (c *Config) ServiceName() string {
	if c.Name != "" {
		return c.Name
	}
	if fields, err := textutil.SplitFields(c.Command, " "); err == nil && len(fields) > 0 {
		return filepath.Base(fields[0])
	}
	return c.File
}

func ParseConfig(filename string) *Config {
	cfg := &Config{File: filename}
	err := config.ParseFile(filepath.Join(servicesDir(), filename), cfg)
	cfg.Err = err
	if cfg.Log == nil {
		cfg.Log = new(Logger)
		cfg.Log.Parse("")
	}
	cfg.Log.Name = cfg.ServiceName()
	return cfg
}

func ParseConfigs() ([]*Config, error) {
	dir := servicesDir()
	if configDirIsDefault() {<commit_msg>If no name is provided, use the filename<commit_after>package main

import (
	"fmt"
	"gnd.la/config"
	"gnd.la/log"
	"gnd.la/util/textutil"
	"io/ioutil"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
	"syscall"
)

type Config struct {
	File             string
	Command          string
	Name             string
	Dir              string
	Environment      map[string]string
	User             string
	Group            string
	Priority         int `default:"1000"`
	Watchdog         *Watchdog
	WatchdogInterval int `default:"300"`
	Log              *Logger
	Err              error
}

func (c *Config) Cmd() (*exec.Cmd, error) {
	if c.Err != nil {
		return nil, c.Err
	}
	if c.Command == "" {
		return nil, fmt.Errorf("no command")
	}
	fields, err := textutil.SplitFields(c.Command, " ")
	if err != nil {
		return nil, err
	}
	if !filepath.IsAbs(fields[0]) {
		p, err := exec.LookPath(fields[0])
		if err != nil {
			return nil, err
		}
		fields[0] = p
	}
	dir := c.Dir
	if dir == "" {
		dir = filepath.Dir(fields[0])
	}
	cmd := &exec.Cmd{Path: fields[0], Args: fields, Dir: dir}
	for k, v := range c.Environment {
		cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", k, v))
	}
	if _, ok := c.Environment["GOMAXPROCS"]; !ok {
		cmd.Env = append(cmd.Env, fmt.Sprintf("GOMAXPROCS=%d", runtime.NumCPU()))
	}
	for _, v := range os.Environ() {
		if p := strings.IndexByte(v, '='); p >= 0 {
			k := v[:p]
			if _, ok := c.Environment[k]; !ok {
				cmd.Env = append(cmd.Env, v)
			}
		}
	}
	info, err := os.Stat(fields[0])
	if err != nil {
		return nil, err
	}
	stat := info.Sys().(*syscall.Stat_t)
	uid := stat.Uid
	gid := stat.Gid
	if c.Group != "" {
		if g := getGroupId(c.Group); g > 0 {
			gid = uint32(g)
		} else {
			return nil, fmt.Errorf("invalid group %q", c.Group)
		}
	}
	if c.User != "" {
		u, err := user.Lookup(c.User)
		if err != nil {
			return nil, err
		}
		ui, _ := strconv.Atoi(u.Uid)
		uid = uint32(ui)
		if gid == 0 {
			gi, _ := strconv.Atoi(u.Gid)
			gid = uint32(gi)
		}
	}
	var cred *syscall.Credential
	if uid != 0 || gid != 0 {
		cred = &syscall.Credential{Uid: uid, Gid: gid}
		attr := &syscall.SysProcAttr{Credential: cred}
		cmd.SysProcAttr = attr
	}
	log.Debugf("%s wd: %s, env: %s, cred: %+v", c.ServiceName(), dir, cmd.Env, cred)
	return cmd, nil
}

func (c *Config) ServiceName() string {
	if c.Name != "" {
		return c.Name
	}
	return c.File
}

func ParseConfig(filename string) *Config {
	cfg := &Config{File: filename}
	err := config.ParseFile(filepath.Join(servicesDir(), filename), cfg)
	cfg.Err = err
	if cfg.Log == nil {
		cfg.Log = new(Logger)
		cfg.Log.Parse("")
	}
	cfg.Log.Name = cfg.ServiceName()
	return cfg
}

func ParseConfigs() ([]*Config, error) {
	dir := servicesDir()
	if configDirIsDefault() {
{'fraction_non_alphanumeric': 0.1760626000355682, 'fraction_numerical': 0.008892050506846879, 'mean_word_length': 2.7418496340652028, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 8, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1480698', 'n_tokens_mistral': 2388, 'n_tokens_neox': 2237, 'n_words': 539}