2024-09-04 12:22:10 +00:00
|
|
|
package s6netdev
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"io/fs"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
type S6SvcTree struct {
|
|
|
|
s map[S6SvcName]*S6Svc
|
|
|
|
rcdir, envdir string
|
|
|
|
root string
|
|
|
|
}
|
|
|
|
|
|
|
|
type kv_file struct {
|
|
|
|
name, value string
|
|
|
|
perm int
|
|
|
|
}
|
|
|
|
|
|
|
|
type S6Svc struct {
|
|
|
|
Name S6SvcName
|
|
|
|
Type S6SvcType
|
|
|
|
Children []*S6Svc // Either dependencies or contents depending on type
|
|
|
|
ProducerFor, ConsumerFor *S6Svc
|
|
|
|
NotifFd int
|
|
|
|
FlagEssential bool
|
|
|
|
Up, Down, Run string
|
|
|
|
env map[string]string
|
|
|
|
}
|
|
|
|
|
|
|
|
type S6SvcName string
|
|
|
|
|
|
|
|
func (s S6SvcName) Sanitize() S6SvcName {
|
|
|
|
// Only sanitize slashes for now
|
|
|
|
return S6SvcName(strings.ReplaceAll(string(s), "/", "_"))
|
|
|
|
}
|
|
|
|
|
|
|
|
type S6SvcType *string
|
|
|
|
|
|
|
|
var S6SvcTypes = struct {
|
|
|
|
Oneshot, Longrun, Bundle string
|
|
|
|
}{
|
|
|
|
Oneshot: "oneshot",
|
|
|
|
Longrun: "longrun",
|
|
|
|
Bundle: "bundle",
|
|
|
|
}
|
|
|
|
|
|
|
|
func S6NewTree() *S6SvcTree {
|
|
|
|
t := new(S6SvcTree)
|
|
|
|
t.s = make(map[S6SvcName]*S6Svc)
|
|
|
|
t.root = "/etc/s6"
|
|
|
|
t.rcdir = "rc"
|
|
|
|
t.envdir = "env"
|
|
|
|
return t
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *S6SvcTree) S6New(name S6SvcName, svc_type S6SvcType) *S6Svc {
|
|
|
|
if name == "" || svc_type == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if e, ok := t.s[name]; ok && e.Type == svc_type {
|
|
|
|
return e
|
|
|
|
}
|
|
|
|
s := new(S6Svc)
|
|
|
|
s.Name = name
|
|
|
|
s.Type = svc_type
|
|
|
|
t.s[name] = s
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
func S6Pipe(src, dst *S6Svc) {
|
|
|
|
src.ProducerFor = dst
|
|
|
|
dst.ConsumerFor = src
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *S6Svc) S6Children(children ...*S6Svc) {
|
|
|
|
s.Children = append(s.Children, children...)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *S6Svc) S6AddEnv(key, value string) {
|
|
|
|
s.env[key] = value
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *S6Svc) S6DelEnv(key string) {
|
|
|
|
delete(s.env, key)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *S6SvcTree) S6AddSvc(s *S6Svc) {
|
|
|
|
t.s[s.Name] = s
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *S6SvcTree) S6GetSvc(name S6SvcName) *S6Svc {
|
|
|
|
return t.s[name]
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *S6SvcTree) S6Services() (r []*S6Svc) {
|
|
|
|
for _, v := range t.s {
|
|
|
|
r = append(r, v)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *S6SvcTree) S6CommitService(s *S6Svc) (err error) {
|
|
|
|
if err = os.Mkdir(t.rcdir, fs.ModeDir|0777); err != nil && !errors.Is(err, os.ErrExist) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
sdir := filepath.Join(t.rcdir, string(s.Name))
|
2024-09-10 14:21:03 +00:00
|
|
|
if err = os.Mkdir(sdir, fs.ModeDir|0777); err != nil && !errors.Is(err, os.ErrExist) {
|
2024-09-04 12:22:10 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var kvfiles []kv_file
|
|
|
|
kvfiles = append(kvfiles, kv_file{"type", *s.Type, 0666})
|
|
|
|
if s.FlagEssential {
|
|
|
|
kvfiles = append(kvfiles, kv_file{"flag-essential", "", 0666})
|
|
|
|
}
|
|
|
|
if s.ProducerFor != nil {
|
|
|
|
kvfiles = append(kvfiles, kv_file{"producer-for", string(s.ProducerFor.Name), 0666})
|
|
|
|
}
|
|
|
|
if s.ConsumerFor != nil {
|
|
|
|
kvfiles = append(kvfiles, kv_file{"consumer-for", string(s.ConsumerFor.Name), 0666})
|
|
|
|
}
|
|
|
|
if s.NotifFd != 0 {
|
|
|
|
kvfiles = append(kvfiles, kv_file{"notification-fd", fmt.Sprintf("%d", s.NotifFd), 0666})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Scripts
|
|
|
|
switch s.Type {
|
|
|
|
case &S6SvcTypes.Longrun:
|
|
|
|
{
|
|
|
|
if s.Run != "" {
|
|
|
|
kvfiles = append(kvfiles, kv_file{"run", s.Run, 0777})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
case &S6SvcTypes.Oneshot:
|
|
|
|
{
|
|
|
|
if s.Up != "" {
|
|
|
|
kvfiles = append(kvfiles, kv_file{"up", s.Up, 0777})
|
|
|
|
}
|
|
|
|
if s.Down != "" {
|
|
|
|
kvfiles = append(kvfiles, kv_file{"down", s.Down, 0777})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, v := range kvfiles {
|
|
|
|
var f *os.File
|
2024-09-10 14:21:03 +00:00
|
|
|
if f, err = os.OpenFile(filepath.Join(sdir, v.name), os.O_CREATE|os.O_RDWR, fs.FileMode(v.perm)); err != nil {
|
2024-09-04 12:22:10 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
_, err = fmt.Fprintln(f, v.value)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Children
|
|
|
|
var dirname string
|
|
|
|
switch s.Type {
|
|
|
|
case &S6SvcTypes.Bundle:
|
|
|
|
dirname = "contents.d"
|
|
|
|
default:
|
|
|
|
dirname = "dependencies.d"
|
|
|
|
}
|
|
|
|
cdir := filepath.Join(sdir, dirname)
|
2024-09-10 14:21:03 +00:00
|
|
|
if err = os.Mkdir(cdir, fs.ModeDir|0777); err != nil && !errors.Is(err, os.ErrExist) {
|
2024-09-04 12:22:10 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, v := range s.Children {
|
|
|
|
var f *os.File
|
|
|
|
if f, err = os.Create(filepath.Join(cdir, string(v.Name))); err != nil && !errors.Is(err, os.ErrExist) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
f.Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(s.env) != 0 {
|
|
|
|
if err = os.Mkdir(t.envdir, fs.ModeDir|0777); err != nil && !errors.Is(err, os.ErrExist) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
sdir := filepath.Join(t.envdir, string(s.Name))
|
2024-09-10 14:21:03 +00:00
|
|
|
if err = os.Mkdir(sdir, fs.ModeDir|0777); err != nil && !errors.Is(err, os.ErrExist) {
|
2024-09-04 12:22:10 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
for k, v := range s.env {
|
|
|
|
var f *os.File
|
|
|
|
if f, err = os.Create(filepath.Join(sdir, string(k))); err != nil && !errors.Is(err, os.ErrExist) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
_, err = fmt.Fprintln(f, v)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|