Files
openlan/pkg/switch/link.go
2025-04-21 10:45:08 +08:00

178 lines
3.2 KiB
Go
Executable File

package cswitch
import (
"os"
"os/exec"
"path/filepath"
"strings"
"sync"
co "github.com/luscis/openlan/pkg/config"
"github.com/luscis/openlan/pkg/libol"
"github.com/luscis/openlan/pkg/models"
nl "github.com/vishvananda/netlink"
)
const (
AccessBin = "openlan-access"
AccessDir = "/var/openlan/access"
)
type Link struct {
cfg *co.Access
out *libol.SubLogger
uuid string
}
func NewLink(cfg *co.Access) *Link {
uuid := libol.GenString(13)
return &Link{
uuid: uuid,
cfg: cfg,
out: libol.NewSubLogger(cfg.Network),
}
}
func (l *Link) Model() *models.Link {
cfg := l.Conf()
return &models.Link{
User: cfg.Username,
Network: cfg.Network,
Protocol: cfg.Protocol,
StatusFile: l.StatusFile(),
}
}
func (l *Link) Initialize() {
file := l.ConfFile()
l.cfg.StatusFile = l.StatusFile()
l.cfg.PidFile = l.PidFile()
_ = libol.MarshalSave(l.cfg, file, true)
}
func (l *Link) Conf() *co.Access {
return l.cfg
}
func (l *Link) UUID() string {
return l.uuid
}
func (l *Link) Path() string {
return AccessBin
}
func (l *Link) ConfFile() string {
return filepath.Join(AccessDir, l.uuid+".json")
}
func (l *Link) StatusFile() string {
return filepath.Join(AccessDir, l.uuid+".status")
}
func (l *Link) PidFile() string {
return filepath.Join(AccessDir, l.uuid+".pid")
}
func (l *Link) LogFile() string {
return filepath.Join(AccessDir, l.uuid+".log")
}
func (l *Link) Start() error {
file := l.ConfFile()
log, err := libol.CreateFile(l.LogFile())
if err != nil {
l.out.Warn("Link.Start %s", err)
return nil
}
libol.Go(func() {
args := []string{
"-alias", l.cfg.Connection + "@" + l.cfg.Network,
"-conf", file,
"-terminal", "ww",
}
l.out.Debug("Link.Start %s %v", l.Path(), args)
cmd := exec.Command(l.Path(), args...)
cmd.Stdout = log
cmd.Stderr = log
if err := cmd.Run(); err != nil {
l.out.Error("Link.Start %s: %s", l.uuid, err)
}
})
return nil
}
func (l *Link) Clean() {
files := []string{
l.LogFile(), l.StatusFile(), l.PidFile(), l.ConfFile(),
}
for _, file := range files {
if err := libol.FileExist(file); err == nil {
if err := os.Remove(file); err != nil {
l.out.Warn("Link.Clean %s", err)
}
}
}
}
func (l *Link) Stop() error {
if data, err := os.ReadFile(l.PidFile()); err != nil {
l.out.Debug("Link.Stop %s", err)
} else {
pid := strings.TrimSpace(string(data))
cmd := exec.Command("kill", pid)
if err := cmd.Run(); err != nil {
l.out.Warn("Link.Stop %s: %s", pid, err)
return err
}
}
l.Clean()
return nil
}
type Links struct {
lock sync.RWMutex
links map[string]*Link
}
func NewLinks() *Links {
return &Links{
links: make(map[string]*Link),
}
}
func (ls *Links) Add(l *Link) {
ls.lock.Lock()
defer ls.lock.Unlock()
ls.links[l.cfg.Connection] = l
}
func (ls *Links) Remove(addr string) *Link {
ls.lock.Lock()
defer ls.lock.Unlock()
if p, ok := ls.links[addr]; ok {
p.Stop()
delete(ls.links, addr)
return p
}
return nil
}
type LinuxLink struct {
link nl.Link
}
func (ll *LinuxLink) Start() error {
if err := nl.LinkAdd(ll.link); err != nil {
return err
}
return nil
}
func (ll *LinuxLink) Stop() error {
if err := nl.LinkDel(ll.link); err != nil {
return err
}
return nil
}