package samba import ( "context" "fmt" "os" "path/filepath" "strings" "github.com/example/storage-appliance/internal/domain" "github.com/example/storage-appliance/internal/infra/osexec" ) type Adapter struct { Runner osexec.Runner IncludePath string } func NewAdapter(runner osexec.Runner, includePath string) *Adapter { if includePath == "" { includePath = "/etc/samba/smb.conf.d/appliance.conf" } return &Adapter{Runner: runner, IncludePath: includePath} } // RenderConf writes the Samba include file for appliance-managed shares func (a *Adapter) RenderConf(ctx context.Context, shares []domain.Share) error { var lines []string lines = append(lines, "# Appliance-managed SMB share configuration") for _, s := range shares { if s.Type != "smb" { continue } opts := []string{"path = " + s.Path} // parse options if stored in s.Name or s.Config; fallback to broad default // s.Config may have read-only or allowed users if ro, ok := s.Config["read_only"]; ok && ro == "true" { opts = append(opts, "read only = yes") } else { opts = append(opts, "read only = no") } if users, ok := s.Config["allowed_users"]; ok { opts = append(opts, "valid users = "+users) } // write section lines = append(lines, fmt.Sprintf("[%s]", s.Name)) for _, l := range opts { lines = append(lines, l) } lines = append(lines, "") } content := strings.Join(lines, "\n") + "\n" dir := filepath.Dir(a.IncludePath) tmp := filepath.Join(dir, ".appliance.smb.tmp") if err := os.WriteFile(tmp, []byte(content), 0644); err != nil { return err } if err := os.Rename(tmp, a.IncludePath); err != nil { return err } return nil } // Reload reloads or restarts samba to apply config func (a *Adapter) Reload(ctx context.Context) error { // try to reload first _, stderr, _, err := osexec.ExecWithRunner(a.Runner, ctx, "systemctl", "reload", "smbd") if err == nil { return nil } // fallback to restart _, stderr, _, err = osexec.ExecWithRunner(a.Runner, ctx, "systemctl", "restart", "smbd") if err != nil { return fmt.Errorf("samba reload/restart failed: %s", stderr) } return nil } // CreateSambaUser optional: stub for creating a local samba user mapped to appliance user func (a *Adapter) CreateSambaUser(ctx context.Context, user, password string) error { // This is optional - we use smbpasswd command in production; stub for now _, stderr, _, err := osexec.ExecWithRunner(a.Runner, ctx, "smbpasswd", "-a", user) if err != nil { return fmt.Errorf("smbpasswd failed: %s", stderr) } return nil }