// Package jailmgr talks to jail(8) directly via jail.conf.d and the base // system's `service jail` rc.d script - no bastille/iocage in the loop. package jailmgr import ( "context" "fmt" "io" "os" "path/filepath" "skiff/internal/layout" "skiff/internal/sh" "skiff/internal/tmpl" ) // WriteConf renders jail.conf.d/.conf and the jail's own rc.d service // script + rc.conf enable line. Called once, from `skiff new-app`. func WriteConf(cfg layout.Config, app, ip, iface string) error { conf, err := tmpl.JailConf(tmpl.JailConfData{Name: app, JailRoot: cfg.JailRoot(app), IP: ip, Iface: iface}) if err != nil { return err } if err := os.WriteFile(cfg.JailConfPath(app), []byte(conf), 0644); err != nil { return fmt.Errorf("write jail.conf: %w", err) } rc, err := tmpl.RCScript(tmpl.RCData{Name: app}) if err != nil { return err } rcPath := cfg.RCScriptPath(app) if err := os.MkdirAll(filepath.Dir(rcPath), 0755); err != nil { return err } if err := os.WriteFile(rcPath, []byte(rc), 0755); err != nil { return fmt.Errorf("write rc.d script: %w", err) } rcConfLine := fmt.Sprintf("\n%s_enable=\"YES\"\n", app) f, err := os.OpenFile(cfg.RCConfPath(app), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { return fmt.Errorf("open jail rc.conf: %w", err) } defer f.Close() if _, err := f.WriteString(rcConfLine); err != nil { return fmt.Errorf("enable service in jail rc.conf: %w", err) } return nil } // EnableMail appends a MAILNAME line to the jail's dma.conf, inherited from // the base image's SMARTHOST/PORT/AUTHPATH/SECURETRANSFER/STARTTLS. Called // once, from `skiff new-app`. func EnableMail(cfg layout.Config, app string) error { f, err := os.OpenFile(cfg.DMAConfPath(app), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { return fmt.Errorf("open jail dma.conf: %w", err) } defer f.Close() if _, err := fmt.Fprintf(f, "\nMAILNAME %s.skiff.local\n", app); err != nil { return fmt.Errorf("set MAILNAME in jail dma.conf: %w", err) } return nil } func Start(ctx context.Context, out io.Writer, app string) error { return sh.Run(ctx, out, "", "service", "jail", "start", app) } // Restart restarts an already-running jail, or starts it if this is its // first deploy and it isn't up yet. func Restart(ctx context.Context, out io.Writer, app string) error { if err := sh.Run(ctx, out, "", "service", "jail", "restart", app); err != nil { return sh.Run(ctx, out, "", "service", "jail", "start", app) } return nil }