Files
kubevpn/pkg/daemon/client.go
2023-10-02 13:44:23 +08:00

167 lines
3.8 KiB
Go

package daemon
import (
"context"
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"time"
log "github.com/sirupsen/logrus"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/health/grpc_health_v1"
_ "google.golang.org/grpc/resolver/dns"
_ "google.golang.org/grpc/resolver/passthrough"
"github.com/wencaiwulue/kubevpn/pkg/config"
"github.com/wencaiwulue/kubevpn/pkg/daemon/rpc"
)
var daemonClient, sudoDaemonClient rpc.DaemonClient
func GetClient(isSudo bool) rpc.DaemonClient {
if _, err := os.Stat(GetSockPath(isSudo)); errors.Is(err, os.ErrNotExist) {
return nil
}
if isSudo && sudoDaemonClient != nil {
return sudoDaemonClient
}
if !isSudo && daemonClient != nil {
return daemonClient
}
sudo := ""
if isSudo {
sudo = "sudo"
}
ctx := context.Background()
conn, err := grpc.DialContext(ctx, "passthrough:///unix://"+GetSockPath(isSudo), grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Errorf("cannot connect to %s server: %v", sudo, err)
fmt.Println(fmt.Errorf("cannot connect to %s server: %v", sudo, err))
return nil
}
c := rpc.NewDaemonClient(conn)
now := time.Now()
healthClient := grpc_health_v1.NewHealthClient(conn)
response, err := healthClient.Check(ctx, &grpc_health_v1.HealthCheckRequest{})
if err != nil {
log.Printf("%v", err)
return nil
}
fmt.Println(response.Status, sudo, time.Now().Sub(now).String())
now = time.Now()
_, err = c.Status(ctx, &rpc.StatusRequest{})
fmt.Printf("call %s api status use %s\n", sudo, time.Now().Sub(now))
if err != nil {
fmt.Println(fmt.Errorf("cannot call %s api status: %v", sudo, err))
log.Error(err)
return nil
}
if isSudo {
sudoDaemonClient = c
} else {
daemonClient = c
}
return c
}
func GetSockPath(isSudo bool) string {
name := config.SockPath
if isSudo {
name = config.SudoSockPath
}
return filepath.Join(config.DaemonPath, name)
}
func GetPidPath(isSudo bool) string {
name := config.PidPath
if isSudo {
name = config.SudoPidPath
}
return filepath.Join(config.DaemonPath, name)
}
func GetDaemonCommand(isSudo bool) *exec.Cmd {
if isSudo {
return exec.Command("sudo", "--preserve-env", os.Args[0], "daemon", "--sudo")
}
return exec.Command(os.Args[0], "daemon")
}
func StartupDaemon(ctx context.Context) error {
// normal daemon
if daemonClient = GetClient(false); daemonClient == nil {
if err := runDaemon(ctx, false); err != nil {
return err
}
}
// sudo daemon
if sudoDaemonClient = GetClient(true); sudoDaemonClient == nil {
if err := runDaemon(ctx, true); err != nil {
return err
}
}
return nil
}
func runDaemon(ctx context.Context, isSudo bool) error {
portPath := GetSockPath(isSudo)
err := os.Remove(portPath)
if err != nil && !errors.Is(err, os.ErrNotExist) {
return err
}
pidPath := GetPidPath(isSudo)
var file []byte
if file, err = os.ReadFile(pidPath); err == nil {
var pid int
if pid, err = strconv.Atoi(strings.TrimSpace(string(file))); err == nil {
var p *os.Process
if p, err = os.FindProcess(pid); err == nil {
if err = p.Kill(); err != nil && err != os.ErrProcessDone {
log.Error(err)
}
}
}
}
cmd := GetDaemonCommand(isSudo)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err = cmd.Start(); err != nil {
return err
}
err = os.WriteFile(pidPath, []byte(strconv.Itoa(cmd.Process.Pid)), os.ModePerm)
if err != nil {
return err
}
err = os.Chmod(GetPidPath(false), os.ModePerm)
if err != nil {
return err
}
go func() {
cmd.Wait()
}()
for ctx.Err() == nil {
time.Sleep(time.Millisecond * 50)
if _, err = os.Stat(portPath); err == nil {
break
}
}
client := GetClient(isSudo)
if client == nil {
return fmt.Errorf("can not get daemon server client")
}
_, err = client.Status(ctx, &rpc.StatusRequest{})
return err
}